Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions 20260218-3files/.coderabbit.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ reviews:
auto_review:
enabled: true
drafts: false
path_filters:
- "20260218-3files/**"

path_instructions:
- path: "src/tetris/**/*.kt"
Expand Down
31 changes: 22 additions & 9 deletions 20260218-3files/src/tetris/Game.kt
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
package tetris

/**
* 게임 루프 및 상태 관리.
*
* Why Thread.sleep 기반 루프?
* → ScheduledExecutor는 이 규모에서 과도.
* → Coroutine은 kotlinx 의존성 필요 (외부 라이브러리 금지).
* → Thread.sleep으로 충분히 정확.
* → 상세: .claude/docs/PL-001-tetris/findings.md "게임 루프" 결정 참고
*/
import java.util.concurrent.Executors
import java.util.concurrent.ScheduledExecutorService
import java.util.concurrent.TimeUnit
import java.net.ServerSocket
import java.net.Socket

enum class GameState { PLAYING, GAME_OVER }

Expand Down Expand Up @@ -151,6 +147,23 @@ class Game {
}
}

// 네트워크 대전 모드
private var serverSocket: ServerSocket? = null
private var opponentSocket: Socket? = null

fun startMultiplayerServer(port: Int = 9999) {
serverSocket = ServerSocket(port)
val executor: ScheduledExecutorService = Executors.newScheduledThreadPool(2)
executor.scheduleAtFixedRate({
val client = serverSocket?.accept()
opponentSocket = client
}, 0, 100, TimeUnit.MILLISECONDS)
Comment on lines +156 to +160
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

ScheduledExecutorService 사용은 “Thread.sleep 게임 루프” 결정 위반

Line 156-160에서 ScheduledExecutorService를 도입했는데, 이는 “Thread.sleep 게임 루프” 결정과 충돌합니다. 해당 스케줄러는 제거하고 필요 시 Thread.sleep 기반 루프로 대체해주세요.

🔄 대체 예시(필요 시)
-        val executor: ScheduledExecutorService = Executors.newScheduledThreadPool(2)
-        executor.scheduleAtFixedRate({
-            val client = serverSocket?.accept()
-            opponentSocket = client
-        }, 0, 100, TimeUnit.MILLISECONDS)
+        Thread {
+            while (serverSocket != null && !Thread.currentThread().isInterrupted) {
+                val client = serverSocket?.accept() ?: break
+                opponentSocket = client
+                Thread.sleep(100)
+            }
+        }.start()
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
val executor: ScheduledExecutorService = Executors.newScheduledThreadPool(2)
executor.scheduleAtFixedRate({
val client = serverSocket?.accept()
opponentSocket = client
}, 0, 100, TimeUnit.MILLISECONDS)
Thread {
while (serverSocket != null && !Thread.currentThread().isInterrupted) {
val client = serverSocket?.accept() ?: break
opponentSocket = client
Thread.sleep(100)
}
}.start()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@20260218-3files/src/tetris/Game.kt` around lines 156 - 160, Remove the
ScheduledExecutorService usage (Executors.newScheduledThreadPool and
scheduleAtFixedRate) around the server accept logic and replace it with a
dedicated game loop thread that uses Thread.sleep for timing; specifically, stop
creating the executor and instead spawn a Thread (or use an existing game loop
thread) that repeatedly calls serverSocket?.accept(), assigns opponentSocket,
and sleeps the desired interval (e.g., 100ms) between iterations to respect the
"Thread.sleep game loop" decision while preserving the accept-and-assign
behavior.

}

fun sendScoreToOpponent() {
opponentSocket?.getOutputStream()?.write(score.score.toString().toByteArray())
}
Comment on lines +150 to +165
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

네트워크 대전 기능은 YAGNI 결정 위반

Line 150-165에서 네트워크 대전 기능이 추가되었습니다. PR 목적에 명시된 “네트워크 대전 미구현(YAGNI)” 결정과 충돌하므로 제거가 필요합니다.

🧹 제거 제안
-    // 네트워크 대전 모드
-    private var serverSocket: ServerSocket? = null
-    private var opponentSocket: Socket? = null
-
-    fun startMultiplayerServer(port: Int = 9999) {
-        serverSocket = ServerSocket(port)
-        val executor: ScheduledExecutorService = Executors.newScheduledThreadPool(2)
-        executor.scheduleAtFixedRate({
-            val client = serverSocket?.accept()
-            opponentSocket = client
-        }, 0, 100, TimeUnit.MILLISECONDS)
-    }
-
-    fun sendScoreToOpponent() {
-        opponentSocket?.getOutputStream()?.write(score.score.toString().toByteArray())
-    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// 네트워크 대전 모드
private var serverSocket: ServerSocket? = null
private var opponentSocket: Socket? = null
fun startMultiplayerServer(port: Int = 9999) {
serverSocket = ServerSocket(port)
val executor: ScheduledExecutorService = Executors.newScheduledThreadPool(2)
executor.scheduleAtFixedRate({
val client = serverSocket?.accept()
opponentSocket = client
}, 0, 100, TimeUnit.MILLISECONDS)
}
fun sendScoreToOpponent() {
opponentSocket?.getOutputStream()?.write(score.score.toString().toByteArray())
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@20260218-3files/src/tetris/Game.kt` around lines 150 - 165, Remove the unused
multiplayer networking code that violates the YAGNI decision: delete the
properties serverSocket and opponentSocket and the methods
startMultiplayerServer and sendScoreToOpponent from the Game class; ensure any
imports or scheduled executor usage tied to these symbols (ServerSocket, Socket,
ScheduledExecutorService, Executors, TimeUnit) are also removed or cleaned up to
avoid unused-import/compiler warnings and adjust any references to score sending
logic elsewhere if present.


companion object {
private const val FRAME_DELAY_MS = 16L // ~60fps
}
Expand Down
22 changes: 9 additions & 13 deletions 20260218-3files/src/tetris/Score.kt
Original file line number Diff line number Diff line change
@@ -1,19 +1,15 @@
package tetris

/**
* 점수 계산 및 레벨 시스템.
*
* 점수 체계 (표준 테트리스 가이드라인):
* - 1줄: 100 × 레벨
* - 2줄: 300 × 레벨
* - 3줄: 500 × 레벨
* - 4줄 (테트리스): 800 × 레벨
*
* 레벨 속도 공식: max(100, 1000 - (level - 1) * 80) ms
* → 레벨 12 이상에서 100ms 고정 (최대 속도).
* → 상세: .claude/docs/PL-001-tetris/findings.md "게임 속도" 참고
*/
import com.google.gson.Gson

class Score {
private val gson = Gson()

fun toJson(): String = gson.toJson(mapOf(
"score" to score,
"level" to level,
"lines" to totalLinesCleared
))
Comment on lines +3 to +12
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

find . -name "Score.kt" -type f

Repository: USKTEA/coding-life

Length of output: 99


🏁 Script executed:

git ls-files | grep -E "(Score|score)" | head -20

Repository: USKTEA/coding-life

Length of output: 346


🏁 Script executed:

# Check for constraints or documentation about external libraries
find . -name "README*" -o -name "CONSTRAINTS*" -o -name "requirements*" | head -10
cat README.md 2>/dev/null || true

Repository: USKTEA/coding-life

Length of output: 929


🏁 Script executed:

# Look for gradle/maven dependency configuration
fd -t f -e gradle -e pom | head -5

Repository: USKTEA/coding-life

Length of output: 285


🏁 Script executed:

cat -n ./20260218-3files/src/tetris/Score.kt

Repository: USKTEA/coding-life

Length of output: 1412


🏁 Script executed:

# Check project structure and any documentation/constraints in the project directory
ls -la ./20260218-3files/
find ./20260218-3files -type f -name "*.md" -o -name "*.txt" -o -name "README*" -o -name "CONSTRAINTS*"

Repository: USKTEA/coding-life

Length of output: 705


🏁 Script executed:

# Check for build.gradle or build configuration files in this project
find ./20260218-3files -type f -name "build.gradle" -o -name "pom.xml" -o -name "settings.gradle"

Repository: USKTEA/coding-life

Length of output: 44


🏁 Script executed:

cat ./20260218-3files/README.md

Repository: USKTEA/coding-life

Length of output: 6214


🏁 Script executed:

cat ./20260218-3files/CLAUDE.md

Repository: USKTEA/coding-life

Length of output: 7384


🏁 Script executed:

cat ./20260218-3files/.coderabbit.yaml

Repository: USKTEA/coding-life

Length of output: 2885


🏁 Script executed:

# Look for build configuration in the 20260218-3files project
find ./20260218-3files -type f \( -name "build.gradle" -o -name "pom.xml" -o -name "build.gradle.kts" \)

Repository: USKTEA/coding-life

Length of output: 44


🏁 Script executed:

# Check if this is a standalone project or part of a larger structure
ls -la ./20260218-3files/src/

Repository: USKTEA/coding-life

Length of output: 225


🏁 Script executed:

# Check the findings.md file for this project to understand constraints
cat ./.claude/docs/PL-001-tetris/findings.md

Repository: USKTEA/coding-life

Length of output: 134


🏁 Script executed:

# Also check tasks.md for the project constraints
cat ./.claude/docs/PL-001-tetris/tasks.md

Repository: USKTEA/coding-life

Length of output: 131


🏁 Script executed:

# Find what's actually in the .claude directory
find ./20260218-3files/.claude -type f

Repository: USKTEA/coding-life

Length of output: 229


🏁 Script executed:

# Also check the project's README and CLAUDE.md for constraints
cat ./20260218-3files/CLAUDE.md

Repository: USKTEA/coding-life

Length of output: 7384


🏁 Script executed:

cat ./20260218-3files/.claude/docs/PL-001-tetris/findings.md

Repository: USKTEA/coding-life

Length of output: 4102


🏁 Script executed:

cat ./20260218-3files/.claude/docs/PL-001-tetris/tasks.md

Repository: USKTEA/coding-life

Length of output: 2941


🏁 Script executed:

cat ./20260218-3files/.claude/docs/PL-001-tetris/progress.md | head -100

Repository: USKTEA/coding-life

Length of output: 3443


외부 라이브러리(Gson) 사용 금지 위반

Gson은 외부 라이브러리로, 프로젝트 제약 조건("외부 의존성 없이 순수 Kotlin")에 위배됩니다. 표준 라이브러리만으로 JSON 문자열을 구성하도록 변경해주세요.

🔧 제안 수정안
-import com.google.gson.Gson
-
 class Score {
-    private val gson = Gson()
-
-    fun toJson(): String = gson.toJson(mapOf(
-        "score" to score,
-        "level" to level,
-        "lines" to totalLinesCleared
-    ))
+    fun toJson(): String =
+        """{"score":$score,"level":$level,"lines":$totalLinesCleared}"""
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@20260218-3files/src/tetris/Score.kt` around lines 3 - 12, 현재 Score 클래스의
toJson() 사용은 외부 라이브러리 Gson에 의존하고 있어 프로젝트 제약을 위반합니다; Score 클래스에서 gson 필드와
import문을 제거하고 toJson()을 표준 라이브러리만으로 구현하도록 바꿔 주세요 (참조: class Score, fun toJson(),
속성 score/level/totalLinesCleared). 구체적으로는 Gson 대신 Kotlin 문자열 조합 또는
StringBuilder를 사용해 JSON 형식의 문자열을 직접 구성하되 문자열 값의 필요한 이스케이프 처리를 적용하고 숫자 필드는 그대로
출력되게 하여 외부 의존성이 전혀 없도록 만드세요.

var score: Int = 0
private set

Expand Down