-
Notifications
You must be signed in to change notification settings - Fork 0
refactor: 입력 처리에 코루틴 기반 비동기 방식 추가 #73
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,22 +1,8 @@ | ||||||||||||||||
| package tetris | ||||||||||||||||
|
|
||||||||||||||||
| import java.util.concurrent.ConcurrentLinkedQueue | ||||||||||||||||
|
|
||||||||||||||||
| /** | ||||||||||||||||
| * 터미널 raw 모드 키보드 입력 처리. | ||||||||||||||||
| * | ||||||||||||||||
| * Why raw 모드 + 별도 스레드? | ||||||||||||||||
| * → System.in.read()는 blocking call. | ||||||||||||||||
| * → 메인 스레드에서 호출하면 게임 루프 정지. | ||||||||||||||||
| * → 별도 daemon 스레드에서 읽고 큐에 적재. | ||||||||||||||||
| * → 상세: .claude/docs/PL-001-tetris/findings.md "입력 처리" 결정 참고 | ||||||||||||||||
| * | ||||||||||||||||
| * 방향키 escape sequence: | ||||||||||||||||
| * → ↑: ESC [ A (27, 91, 65) | ||||||||||||||||
| * → ↓: ESC [ B (27, 91, 66) | ||||||||||||||||
| * → →: ESC [ C (27, 91, 67) | ||||||||||||||||
| * → ←: ESC [ D (27, 91, 68) | ||||||||||||||||
| */ | ||||||||||||||||
| import kotlinx.coroutines.* | ||||||||||||||||
| import kotlinx.coroutines.channels.Channel | ||||||||||||||||
|
|
||||||||||||||||
| enum class Action { | ||||||||||||||||
| LEFT, RIGHT, ROTATE, SOFT_DROP, HARD_DROP, QUIT, NONE | ||||||||||||||||
|
|
@@ -86,4 +72,33 @@ class Input { | |||||||||||||||
| fun stop() { | ||||||||||||||||
| running = false | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| private val scope = CoroutineScope(Dispatchers.IO) | ||||||||||||||||
| private val channel = Channel<Action>(Channel.CONFLATED) | ||||||||||||||||
|
Comment on lines
+76
to
+77
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Channel 리소스 관리 누락
🤖 Prompt for AI Agents |
||||||||||||||||
|
|
||||||||||||||||
| fun startAsync() { | ||||||||||||||||
| scope.launch { | ||||||||||||||||
| while (isActive) { | ||||||||||||||||
| val b = withContext(Dispatchers.IO) { System.`in`.read() } | ||||||||||||||||
| if (b == -1) continue | ||||||||||||||||
| val action = when (b) { | ||||||||||||||||
| 27 -> parseEscapeSequence() | ||||||||||||||||
| 32 -> Action.HARD_DROP | ||||||||||||||||
| 113, 81 -> Action.QUIT | ||||||||||||||||
| else -> Action.NONE | ||||||||||||||||
| } | ||||||||||||||||
| if (action != Action.NONE) { | ||||||||||||||||
| channel.send(action) | ||||||||||||||||
| } | ||||||||||||||||
| } | ||||||||||||||||
| } | ||||||||||||||||
| } | ||||||||||||||||
|
Comment on lines
+79
to
+95
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
가이드라인 위반 이슈와 별개로, 구현 자체에도 문제가 있습니다. Line 82에서 만약 코루틴 사용이 허용된다면 수정 제안 fun startAsync() {
scope.launch {
while (isActive) {
- val b = withContext(Dispatchers.IO) { System.`in`.read() }
- if (b == -1) continue
- val action = when (b) {
- 27 -> parseEscapeSequence()
- 32 -> Action.HARD_DROP
- 113, 81 -> Action.QUIT
- else -> Action.NONE
- }
+ val action = withContext(Dispatchers.IO) {
+ val b = System.`in`.read()
+ if (b == -1) return@withContext Action.NONE
+ when (b) {
+ 27 -> parseEscapeSequence()
+ 32 -> Action.HARD_DROP
+ 113, 81 -> Action.QUIT
+ else -> Action.NONE
+ }
+ }
if (action != Action.NONE) {
channel.send(action)
}
}
}
}🤖 Prompt for AI Agents |
||||||||||||||||
|
|
||||||||||||||||
| suspend fun pollAsync(): Action { | ||||||||||||||||
| return channel.tryReceive().getOrNull() ?: Action.NONE | ||||||||||||||||
| } | ||||||||||||||||
|
Comment on lines
+97
to
+99
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 불필요한
수정 제안- suspend fun pollAsync(): Action {
+ fun pollAsync(): Action {
return channel.tryReceive().getOrNull() ?: Action.NONE
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||
|
|
||||||||||||||||
| fun stopAsync() { | ||||||||||||||||
| scope.cancel() | ||||||||||||||||
| } | ||||||||||||||||
|
Comment on lines
+101
to
+103
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 리소스 정리 불완전
수정 제안 fun stopAsync() {
scope.cancel()
+ channel.close()
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||
| } | ||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
코딩 가이드라인 위반: kotlinx-coroutines 외부 의존성 사용 불가
프로젝트 코딩 가이드라인에 명시된 바와 같이, 이 프로젝트는 순수 Kotlin 표준 라이브러리만 사용해야 하며
kotlinx-coroutines와 같은 외부 의존성 도입이 금지되어 있습니다. 타이밍 제어에는Thread.sleep()을, 비동기 입력 처리에는 기존 Thread 기반 방식을 사용해야 합니다.코루틴 기반 비동기 API 대신 기존 Thread + ConcurrentLinkedQueue 패턴을 확장하는 방식을 고려해 주세요.
As per coding guidelines: "Use pure Kotlin standard library only - do not introduce external dependencies like kotlinx-coroutines, use Thread.sleep() for timing control instead of ScheduledExecutor or Coroutines"
🤖 Prompt for AI Agents