이 글은 Today I Learn 시리즈의 62번째 기록입니다. (총 125개)

색이 헷갈린다는 말에서 시작해서

비슷한 색은 합치기로 했다

플레이하다 보면 두 바스켓이 같은 색으로 보이는데 서로 다른 색으로 취급되는 판이 있었다. 그 색을 부으려고 눌렀는데 그 색이 아닌 것이다.

도안은 사진에서 색을 몇 개로 줄여 만든다. 줄이는 과정에서 사람 눈에는 같은 두 색이 따로 남는 일이 생긴다. 그러니 일정 거리보다 가까운 색은 아예 합치기로 정했다.

  • 사람이 구별 못 하는 차이를 시스템이 구별하면 그건 기능이 아니라 함정이다
  • 어디까지가 같은 색인지는 코드가 못 정한다. 눈으로 보고 선을 그어 줘야 한다

합쳤는데도 쌍둥이가 계속 나왔다

합치기를 넣었는데 여전히 같은 색 두 개가 나오는 도안이 있었다.

이유를 보니 합친 뒤에 오는 단계들이 색을 다시 움직이고 있었다. 흰색을 살리는 처리, 배경과 대비를 확보하는 처리가 각자 색을 밀어서, 앞에서 합쳐 놓은 것 옆에 새 쌍둥이를 만들어 냈다.

합치는 자리를 그 뒤로 옮겼다.

  • 값을 움직이는 단계가 있으면 정리는 그 뒤에 한다. 앞에서 아무리 정리해도 뒤에서 다시 흐트러진다
  • 결과가 이상하면 각 단계를 의심하기 전에 순서를 먼저 본다

그래도 부족했다 — 두 규칙이 서로를 깨고 있었다

뒤로 옮겨도 여전히 걸리는 도안이 있었다. 두 규칙이 서로를 무너뜨리고 있었기 때문이다.

두 색을 합쳐 평균을 내면 그 색이 배경과 가까워질 수 있다. 배경에서 떼어 놓으려고 색을 어둡게 밀면 이번엔 다른 색과 붙는다. 한쪽을 고치면 다른 쪽이 어긋난다.

순서로는 해결이 안 된다고 보고, 더 이상 겹치는 게 없을 때까지 두 규칙을 번갈아 돌리게 했다.

  • 서로의 결과를 깨는 규칙이 둘 있으면 순서를 고민할 게 아니라 수렴할 때까지 반복해야 한다
  • 반복하는 구조에는 빠져나올 조건과 최대 횟수를 같이 둔다

여기에 규칙을 하나 더 넣은 게 문제였다

합치기 기준에는 안 걸리지만 여전히 헷갈리는 색쌍이 남아 있었다. 그래서 그런 쌍은 밝은 쪽을 더 밝게, 어두운 쪽을 더 어둡게 벌리자는 규칙을 하나 더 넣기로 했다.

그럴듯해 보였는데 이 게임의 배경은 늘 밝은 파스텔이다. 밝은 쪽을 더 밝게 미는 건 배경 쪽으로 미는 것이었다.

  • 새 규칙을 더할 때는 이미 있는 규칙과 방향이 부딪히는지 본다. 각각은 맞아도 같이 돌면 반대로 간다

여섯 개만 고쳤으면 몰랐을 일

내가 짚은 사례는 여섯 개였다. 그것만 고치고 넘어갈 수도 있었는데, 기준을 숫자로 정하고 200레벨을 전부 다시 쟀다.

51개가 깨져 있었다. 방금 넣은 그 규칙 때문이었다.

버렸다. 내가 짚은 것들은 전부 합치기만으로 잡히는 거리라 애초에 없어도 될 규칙이었다.

  • 사례 몇 개를 고칠 때는 고치면서 깬 것을 볼 방법이 같이 있어야 한다. 없으면 나아졌는지 나빠졌는지 알 수가 없다
  • “고쳤다”는 느낌과 실제로 고쳐졌는지는 다르다
  • 규칙을 더하기 전에 지금 있는 것으로 안 되는지 먼저 확인한다. 더한 규칙은 언제나 무언가를 깬다

지키기로 한 것은 다시 훼손하지 않는다

흰색을 살려 놓고, 배경과 대비가 부족하다고 그 흰색을 다시 어둡게 누르면 원점이다. 흰색은 건드리지 않고 배경을 내리는 쪽으로 정했다.

여기에도 끝을 정해야 했다. 배경을 계속 내리면 결국 전경색과 같아진다.

  • 규칙 사이에 우선순위를 정해 둔다. 무엇을 지킬지 정하면 나머지가 양보할 쪽을 안다
  • 한 방향으로 계속 미는 처리에는 바닥을 둔다. 안 그러면 극단에서 뒤집힌다

척도가 무너지는 자리

백조 몸통이 회색으로 나왔다

흰색이어야 할 자리가 회색이었다.

“이 색이 얼마나 선명한가”를 재는 흔한 공식이 밝은 색에서 값을 부풀린다는 게 원인이었다. 눈으로 보면 그냥 흰색인 백조 몸통이 “채도 55%인 선명한 색”으로 분류돼 흰색 처리를 못 받았다.

밝기에 안 흔들리는 척도로 바꾸고, 거기에 “충분히 밝은가”를 같이 걸었다. 검정도 색기가 없으니 그 조건이 없으면 검정까지 흰색으로 잡힌다.

  • 값이 이상하면 대상이 아니라 재는 방식을 의심한다. 대부분 구간에서 맞는 척도가 특정 구간에서 뒤집히는 경우가 있다
  • 하나로 판정이 안 되면 조건을 둘로 나눈다. “밝다”와 “색기가 없다”는 다른 이야기고, 흰색은 둘 다여야 한다

손으로 적은 데이터는 검사를 안 거친다

자동 처리를 다 고쳤는데도 몇 레벨이 계속 걸렸다.

그것들은 사진에서 뽑은 게 아니라 코드로 그린 도형이었다. 색을 손으로 적어 둬서 자동 검사를 아예 안 거치고 있었다. 같은 잣대로 전부 다시 재서 고쳤다.

  • 자동화한 규칙이 있으면 손으로 넣은 데이터도 그 규칙에 넣어 본다. 예외로 둔 것이 나중에 유일하게 남은 문제가 된다

번호로 지목할 수 없게 됐다

색을 강하게 합쳤더니 도형 열 종이 색 하나로 뭉개져 자동으로 탈락했다. 색 하나짜리는 레벨로도 의미가 없으니 받아들였다.

그런데 풀에서 열 종이 빠지자 레벨 번호와 도안의 짝이 통째로 밀렸다. 120번이 자명종에서 판다로 바뀌는 식이다.

  • 배정이 자동이면 “120번 고쳐 줘”라고 말할 수 없다. 도안 이름으로 말해야 한다
  • 확인도 특정 번호가 아니라 전수로 해야 한다. 번호는 다음 생성에서 다른 것을 가리킨다

상태를 어디에 표시할까

색을 다 정리했는데 또 색 이야기가 나왔다

팔레트를 다 손봤는데도 테스트해 준 사람이 비활성 모래가 어두워져서 색이 달라 보인다고 했다.

못 꺼내는 바스켓을 회색과 섞어 흐리게 만들고 있었다. 빨강을 회색과 절반 가까이 섞으면 탁한 벽돌색이 된다. 쌍둥이 색을 없애느라 한 일을 이 처리 하나가 다시 뭉개고 있었던 것이다.

  • 색이 정보를 지고 있는 화면에서 상태를 색으로 표현하면, 상태를 얻는 대신 정보를 잃는다
  • 상태를 나타낼 축은 색 말고도 있다. 방향·위치·크기·움직임이 다 후보다

같은 그림에 두 뜻을 주지 않는다

상태를 방향으로 옮기기로 했다. 못 꺼내는 통은 엎어 두고, 꺼낼 수 있게 되면 뒤집히며 열린다.

그런데 벨트에는 이미 “부을 수 있으면 기울어 쏟는” 연출이 있었다. 여기에 “뒤집힘은 못 쓴다”를 더하면 같은 그림이 정반대 두 뜻을 갖는다.

엎기를 다른 축으로 잡았다. 쏟기는 옆으로 기울고 엎기는 앞뒤로 넘어간다. 실루엣이 달라 헷갈리지 않는다.

  • 시각 언어도 이름과 같다. 하나에 두 뜻이 붙으면 매번 문맥을 따져야 한다
  • 새 표현을 더할 때 기존 표현과 겹치는지 먼저 본다. 겹치면 축을 바꾼다

안 보여도 읽혀야 하는 정보가 있다

엎어 둔 통은 안이 안 보인다. 그래도 통 자체는 그 색으로 물들여 두기로 했다.

아직 못 꺼내는 색까지 보고 순서를 계획하기 때문이다. 안 보이게 하는 것과 정보를 감추는 것은 다르다.

  • 무언가를 가릴 때 그것이 어떤 판단에 쓰이는지 먼저 본다. 계획에 쓰이는 정보를 가리면 게임이 운이 된다

한꺼번에 움직이면 정신없다

통 하나를 꺼내면 길이 다시 열려 여러 개가 동시에 꺼낼 수 있게 된다. 대여섯 개가 똑같이 뒤집히니 어수선했다.

뒤집히는 속도를 바스켓마다 조금씩 다르게 흩으니 물결처럼 보였다.

그리고 판에 들어가는 첫 화면에서는 아예 안 움직이게 했다. 시작하자마자 못 꺼내는 통이 우르르 엎어지는 건 연출이 아니라 소란이다.

  • 같은 연출이 여럿 동시에 일어나면 하나씩 볼 때보다 나빠진다. 시작이나 속도를 흩는다
  • 처음 보이는 화면은 이미 정답인 상태여야 한다. 초기 상태를 연출로 만들면 매번 그걸 봐야 한다

두 곳이 같은 판단을 하면 언젠가 갈라진다

바닥이 빛나는 조건과 통이 뒤집히는 조건은 같은 것을 나타낸다. 식이 어긋나면 “바닥은 빛나는데 통은 엎어져 있는” 모순이 화면에 보인다.

지금은 주석으로 묶어 뒀지만 이런 자리가 늘면 판단을 한 곳으로 올려야 한다.

  • 같은 것을 두 곳에서 판단하면 처음에는 맞고 나중에 갈라진다. 갈라진 것은 화면에서 모순으로 나타난다

Series: Today I Learn

1 C++ 자료형(Data Type) 2 MD5 vs pHash 3 C++에서 함수의 선언과 정의 4 Tkinter padx, pady 5 메모리와 포인터 변수 6 Call by Value, Call by Reference, Call by Pointer 비교 7 const 8 Gemfile — Jekyll 프로젝트의 의존성 파일 9 kramdown-parser-gfm — Jekyll의 GFM 파서 10 파서(Parser) 11 AHU vs OHU 12 I might try it vs I'll try it 뉘앙스 차이 13 SESSION_EXPIRE_AT_BROWSER_CLOSE=True 14 configuration key 15 Git stash vs discard 16 subprocess.Popen으로 Windows 탐색기에 명령어를 전달 17 Post 잔디 분석하기 18 Google Sheets Sync 최적화 19 DSL (Domain Specific Language)과 GPL (General Purpose Language) 20 마크다운 표 그리는 방법 21 쿼리 파라미터(Query Parameter). 기존 QR코드 재활용 22 Django 보안 취약점 점검 및 수정 23 OOP Object-Oriented Programming 객체 지향 프로그래밍 24 Fernet 대칭 암호화 25 Jekyll 코드블록 안의 Liquid 태그 26 insertOnConflictUpdate vs DoUpdate(target) 27 세션 필터 28 아코디언(Accordiaon) UI를 펼친상태로 만들기 29 input의 step 30 Word Cloud 31 Google Sheets를 데이터 버스로(with AppSheet) 32 Django 모델 텍스트 필드 자동 수집 패턴 33 localStorage로 섹션 토글 상태 유지 34 순차 ID 생성(`select_for_update()` + `max()` 조합) 35 역참조 검색과 distinct() 36 xlsx 다운로드와 로딩 오버레이 충돌 37 Android 파일 공유 MIME 타입 38 AssetManifest — Flutter 빌드 타임 asset 목록 런타임 조회 39 UTF-8 BOM과 PowerShell 파일 쓰기 40 소리꽃 KeyBloom TIL 1 41 소리꽃 KeyBloom TIL 2 42 소리꽃 KeyBloom TIL 3 43 메트로놈 Simple Metronome TIL 1 44 소리꽃 KeyBloom TIL 4 45 메트로놈 Simple Metronome TIL 2 46 소리꽃 KeyBloom TIL 5 47 메트로놈 Simple Metronome TIL 3 48 정적 블로그 SEO 정비와 Pagefind 검색 도입 49 메트로놈 Simple Metronome TIL 4 50 안드로이드 AudioTrack 연속 재생, 실측 피커 정렬, 카메라 토치 플래시 51 메트로놈 Simple Metronome TIL 5 52 모래게임 Sandrop TIL 1 53 메트로놈 Simple Metronome TIL 6 54 모래게임 Sandrop TIL 2 55 모래게임 Sandrop TIL 3 56 메트로놈 Simple Metronome TIL 7 57 모래게임 Sandrop TIL 4 58 모래게임 Sandrop TIL 5 59 모래게임 Sandrop TIL 6 60 소리꽃 KeyBloom TIL 6 61 모래게임 Sandrop TIL 7 62 모래게임 Sandrop TIL 8 읽는 중 63 소리꽃 KeyBloom TIL 7 64 모래게임 Sandrop TIL 9 65 소리꽃 KeyBloom TIL 8 66 모래게임 Sandrop TIL 10 67 소리꽃 KeyBloom TIL 9 68 소리꽃 KeyBloom TIL 10 69 소리꽃 KeyBloom TIL 11 70 소리꽃 KeyBloom TIL 12 71 소리꽃 KeyBloom TIL 13 72 온실 GreenHouse TIL 1 73 소리꽃 KeyBloom TIL 14 74 온실 GreenHouse TIL 2 75 소리꽃 KeyBloom TIL 15 76 소리꽃 KeyBloom TIL 16 77 소리꽃 KeyBloom TIL 17 78 소리꽃 KeyBloom TIL 18 79 소리꽃 KeyBloom TIL 19 80 소리꽃 KeyBloom TIL 20 81 로그스톤 상표 셀프 출원 TIL 1 82 소리꽃 KeyBloom TIL 21 83 소리꽃 KeyBloom TIL 22 84 소리꽃 KeyBloom TIL 23 85 소리꽃 KeyBloom TIL 24 86 MiniMacro TIL 1 87 Astro가 무엇인지, 왜 옮기는지 88 MiniMacro TIL 2 89 메트로놈 Simple Metronome TIL 8 90 메트로놈 Simple Metronome TIL 9 91 블로그 Astro 이관 TIL 92 오픈데이 Openday TIL 1 93 오픈데이 Openday TIL 2 94 Unreal Engine MCP TIL 1 95 콘티온 Conti On TIL 11 96 오픈데이 Openday TIL 3 97 오픈데이 Openday TIL 4 98 오픈데이 Openday TIL 5 99 ScorePlayer TIL 1 100 BuildingHub TIL 1 101 ScorePlayer TIL 2 102 BuildingHub TIL 2 103 ScorePlayer TIL 3 104 Unreal Engine MCP TIL 2 105 궁 미로 PalaceMaze TIL 1 106 메트로놈 Simple Metronome TIL 10 107 메트로놈 Simple Metronome TIL 11 108 오픈데이 Openday TIL 6 109 BuildingHub TIL 3 110 Obsidian vault에서 코드만 빼기 — directory junction 111 ScorePlayer TIL 4 112 오픈데이 Openday TIL 7 113 URL은 바뀔 수 있는 것에 묶지 않는다 114 도구는 기능이 아니라 내 일에 맞는지로 고른다 115 돌이키기 힘든 변경은 작게 먼저 확인한다 116 궁 미로 PalaceMaze TIL 3 117 궁 미로 PalaceMaze TIL 2 118 소리꽃 KeyBloom TIL 29 119 오픈데이 Openday TIL 8 120 BuildingHub TIL 4 121 ScorePlayer TIL 5 122 BuildingHub TIL 5 123 MiniMacro TIL 3 124 소리꽃 KeyBloom TIL 30 125 궁 미로 PalaceMaze TIL 4