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

오늘 한 일

  • 실시간 순환 컨베이어 벨트 + 엘리베이터 리워크 완성
    • 통합 플레이 필드(엘리베이터·컨베이어·캔버스·보드를 한 좌표계에) + 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칸 압력)라, 분할 수를 레벨 고정이 아니라 배치 탐색 차원에 넣음
    • 남은 한계: 23색 내장 도안은 덜 막혀 easy/normal이 목표 밴드 위로 뜸 → 색 48 이미지 양자화 파이프라인이 들어오면 풀림
  • 되돌리기가 구조적으로 한 번도 될 수 없었다 — 버튼은 “벨트가 완전히 빈 상태”에서만 켜지는데 엔진은 “되돌릴 바스켓이 레일에 살아 있을 것”을 요구. 실시간 벨트에선 둘이 동시에 참일 수 없어 버튼이 늘 안 눌렸다(즉시 붓던 시절 undo 설계를 조금씩 붓기로 옮기며 생긴 모순)
    • 해결: 이동 중(엘리베이터 상승 중) 바스켓도 엔진이 인지하고 스냅샷이 그 순간을 기록하게 해, 벨트가 도는 중에도 붓는 중에도 되돌릴 수 있게 함. 같은 원인으로 이어하기가 모래를 소실시키던 잠재 버그도 함께 잡힘
  • 시뮬레이터가 난이도 측정을 감당 못 했다 — 티어를 정하려면 레벨마다 수십 번 시뮬레이션해야 하는데, 64×64에서 그레인마다 전체 픽셀을 훑으니 분 단위로 걸렸다
    • 해결: 붓기 불변식(열마다 최하단부터 연속으로 참)을 이용해 열별 높이만 추적해 탐색을 크게 줄임. 측정은 작은 프로브 캔버스로 빠르게, 클리어 가능 보장만 풀 해상도로 분리

다음에 할 일

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

Series: Sandrop

1 모래게임 Sandrop 0 2 모래게임 Sandrop 1 3 모래게임 Sandrop 2 읽는 중 4 모래게임 Sandrop 3 5 모래게임 Sandrop 4 6 모래게임 Sandrop 5 7 모래게임 Sandrop 6 8 모래게임 Sandrop 7 9 모래게임 Sandrop 8 10 모래게임 Sandrop 9 11 모래게임 Sandrop 10 12 모래게임 Sandrop 11 13 모래게임 Sandrop 12 14 모래게임 Sandrop 13 15 모래게임 Sandrop 14 16 모래게임 Sandrop 15 17 모래게임 Sandrop 16 18 모래게임 Sandrop 17 19 모래게임 Sandrop 18 20 모래게임 Sandrop 19 21 모래게임 Sandrop 20 22 모래게임 Sandrop 21 23 모래게임 Sandrop 22 24 모래게임 Sandrop 23 25 모래게임 Sandrop 24 26 모래게임 Sandrop 25 27 모래게임 Sandrop 26 28 모래게임 Sandrop 27 29 모래게임 Sandrop 28 30 모래게임 Sandrop 29 31 모래게임 Sandrop 30 32 모래게임 Sandrop 31 33 모래게임 Sandrop 32 34 모래게임 Sandrop 33 35 모래게임 Sandrop 34 36 모래게임 Sandrop 35 37 모래게임 Sandrop 36 38 모래게임 Sandrop 37

Suggested Posts