The final cleanup is to make every terminal path use one shared payout and lifecycle flow.
Why Centralize End State:
Wins, draws, and forfeits all end the game. They should not each hand-edit winner , credits , stake , and lifecycle flags.
Use _endGame:makeMove should call _endGame(msg.sender, false) for a win and _endGame(address(0), true) for a draw.
Forfeit Path:claimForfeit should also call _endGame(msg.sender, false) . That avoids duplicated payout math and keeps timeout wins consistent with board wins.
Keep checkWin:
The win helper is named checkWin() throughout the course. Keep that helper name in every end-state branch.
Your task: Refactor the end-state paths so the contract has one source of truth for payouts, lifecycle cleanup, and GameEnded while keeping the completed board readable.