이 글은 Sandrop 시리즈의 3번째 기록입니다. (총 23개)

오늘 한 일

  • 실시간 순환 컨베이어 벨트 + 엘리베이터 리워크 완성
    • 통합 플레이 필드(엘리베이터·컨베이어·캔버스·보드를 한 좌표계에) + Ticker 게임 루프
    • 벨트는 계속 좌→우로 돌고, 바스켓은 슬롯 레인(1/N 간격)에 실려 겹치지 않게
      • 이유: 슬롯 위치를 각자 계산하면 속도 차이로 겹침 — 벨트 전체가 하나의 위상(offset)을 공유하면 구조적으로 못 겹침
    • 왼쪽 진입구에 빈 레인이 왔을 때만 장착, 탑승 조건은 컨베이어+대기 합 < 레일 용량
    • 빈 벨트에서 오프셋이 멈춰 두 번째 바스켓부터 안 타던 버그 수정
  • 벽(walls) 규칙 추가 — §3.1 스펙 개정
    • 보드 칸 종류를 빈칸·바스켓·벽 3가지로 확장. 벽은 flood-fill R에서 제외되는 영구 통과 불가 칸
      • 이유: 통로 구조를 설계할 도구가 없어 보드가 무작위 흩뿌리기 그 이상이 될 수 없었음 — 재미 없음의 원인
    • Dart·Python 양쪽 엔진 반영 + 공통 fixtures 케이스 5(벽) 추가로 규칙 일치 검증
  • 레벨 생성기를 설계 기반으로 전면 재작성
    • 벽 패턴 4종(빗살 상/하·주머니·기둥) + 전 칸 연결성 검증
    • 매장 깊이(peel depth) 밴드 + 아래 행 편향 배치로 파내는 순서를 강제
    • ε-greedy 봇 클리어율로 §6 티어 밴드 탐색 — 튜토리얼 5×5 → 쉬움 6×6 → 보통 7×7, 10의 배수 hard·25의 배수 veryhard 톱니 곡선
    • 다색 도형 2종(rings·waves)과 5색 팔레트 추가, hard 티어는 다색 풀 전용
      • 이유: 2색 도안은 데드락이 사실상 안 나서 상위 티어 밴드에 도달 불가
    • 레벨 50개 재생성, 클리어 가능은 풀 해상도 그리디 봇 + Dart 교차검증으로 보장
  • UI 리워크 — 리포트의 불평 4건 반영
    • 탭한 바스켓이 BFS 경로를 따라 통로를 걸어 출구로 나가는 연출 (길찾기가 눈에 보이게)
    • 붓는 중인 바스켓 → 캔버스 목표 픽셀로 떨어지는 모래 줄기
    • 벨트 트랙에 흐르는 사선 트레드 + 양끝 하우징
    • 보드를 모래 트레이로 — 파낸 굴(통로)·바위(벽)·잔량이 차 있는 양동이(바스켓)
    • 우측 대기 스트립 상시 표시 제거(버퍼 있을 때만 오버레이 칩) + 캔버스 동적 확대로 여백 회수
    • 모래 공방 웜톤 팔레트 + 배경 그라데이션
  • 온보딩
    • 이유: 남편한테 아무 설명 없이 그냥 플레이해보게 했더니 “설명이 있으면 좋겠다”고 했다. 안내 없이는 규칙을 알아채기 어려웠던 것 — 첫 실사용자 반응이라 바로 반영
    • 레벨 1 튜토리얼 배너 2단계 + 비네팅(선택 가능한 바스켓만 밝히고 나머지 터치 흡수)
    • 레벨 6/7/8 첫 진입 시 되돌리기/매그넷/셔플 2개씩 지급 + 버튼 펄스로 사용 유도
    • 아이템 인벤토리 신설 — 보유분 우선 소모, 없으면 코인 결제. 지급 이력은 델타 원장 grant 마커로 중복 방지
  • 되돌리기 살리기
    • 벨트 리워크 이후 구조적으로 한 번도 동작할 수 없던 버그를 발견·수정 (아래 「막힌 부분」)
    • 사용 시 복귀한 바스켓 칸에 강조 링을 띄워 무엇이 되돌아왔는지 보이게
    • 아이템 3종 모두 사용 불가 조건이면 버튼 비활성화
  • 티어 라벨(HARD/VERY HARD)을 HUD·레벨 선택에 표시, 붓기 속도 하향(520→360), 걷기 속도 상향(9→14칸/초)
  • E2E 위젯 테스트 추가 — 탭→걷기→상승→장착→붓기→완성 오버레이가 실제 게임 루프에서 완주하는지 검증 (전체 93개 통과)

막힌 부분

봇·정합성·성능 최적화의 구체적인 구현·코드는 TIL “모래게임 Sandrop TIL 1”에 정리했다.

  • 난이도를 잴 “봇”이 정의돼 있지 않았다 — §6 난이도 표는 봇 클리어율 기준인데 어떤 봇으로 재는지가 없었다. 보장용 그리디 봇은 쉬움~보통이 전부 100%로 붙어 변별이 안 되고, 균등 무작위 봇은 다색 레벨이 전부 0%라 상위 티어가 도달 불가
    • 해결: “가끔 실수하는 평균 플레이어” 모델(한 수마다 일정 확률로 우선순위를 잊는 봇)로 중간 지대를 만듦
    • 측정으로 얻은 것: 클리어율의 진짜 지렛대가 벽이 아니라 분할 정도(바스켓 수 대 레일 5칸 압력)라, 분할 수를 레벨 고정이 아니라 배치 탐색 차원에 넣음
    • 남은 한계: 2~3색 내장 도안은 덜 막혀 easy/normal이 목표 밴드 위로 뜸 → 색 4~8 이미지 양자화 파이프라인이 들어오면 풀림
  • 되돌리기가 구조적으로 한 번도 될 수 없었다 — 버튼은 “벨트가 완전히 빈 상태”에서만 켜지는데 엔진은 “되돌릴 바스켓이 레일에 살아 있을 것”을 요구. 실시간 벨트에선 둘이 동시에 참일 수 없어 버튼이 늘 안 눌렸다(즉시 붓던 시절 undo 설계를 조금씩 붓기로 옮기며 생긴 모순)
    • 해결: 이동 중(엘리베이터 상승 중) 바스켓도 엔진이 인지하고 스냅샷이 그 순간을 기록하게 해, 벨트가 도는 중에도 붓는 중에도 되돌릴 수 있게 함. 같은 원인으로 이어하기가 모래를 소실시키던 잠재 버그도 함께 잡힘
  • 시뮬레이터가 난이도 측정을 감당 못 했다 — 티어를 정하려면 레벨마다 수십 번 시뮬레이션해야 하는데, 64×64에서 그레인마다 전체 픽셀을 훑으니 분 단위로 걸렸다
    • 해결: 붓기 불변식(열마다 최하단부터 연속으로 참)을 이용해 열별 높이만 추적해 탐색을 크게 줄임. 측정은 작은 프로브 캔버스로 빠르게, 클리어 가능 보장만 풀 해상도로 분리

다음에 할 일

  • 실기기 확인 — 걷기(14칸/초)·붓기(360그레인/초) 체감 속도, 비네팅 명도, 웜톤 색감, 티어 뱃지 시인성
  • 로프·물음표 기믹 엔진 반영 (레벨 15~/30~ 등장 스케줄)
  • 이미지 양자화 파이프라인(quantize.py) — 색 수 4~8 확보로 easy/normal 클리어율 밴드 정합 개선
  • M4 수익화 — AdMob 전면·보상형 + IAP + No Ads

Series: Sandrop


Suggested Posts