implement game turn logic

This commit is contained in:
2026-11-02 21:24:32 +03:00
parent 153dc74ff6
commit 189b027a11
7 changed files with 896 additions and 13 deletions
+54 -1
View File
@@ -12,8 +12,10 @@
// 3 S->C STATE [3, <seq>, <state_code>]
// 4 C<->S ACK [4, <seq>]
// 5 C->S PLACE_SHIPS [5, ship0_orientation, ship0_x, ship0_y, ...]
// 6 C->S FIRE [6, <x>, <y>]
// 7 S->C FIRE_RESULT [7, <seq>, <result_code>]
//
// Every state-changing server->client message (END, STATE) carries a
// Every state-changing server->client message (END, STATE, FIRE_RESULT) carries a
// per-player monotonically increasing sequence number. The client confirms
// delivery with an ACK message containing the sequence number of the last
// received message (ACK is cumulative). Unacknowledged messages are kept in
@@ -36,6 +38,8 @@ const (
OpState = 3 // S->C: game state transition
OpAck = 4 // C<->S: delivery acknowledgement
OpPlaceShips = 5 // C->S: ship placement
OpFire = 6 // C->S: fire shot
OpFireResult = 7 // S->C: shot result
)
// Board and ship configuration constants.
@@ -89,9 +93,19 @@ const (
StateWaitOpponentTurn = 5 // wait for the opponent's move
)
// Shot result codes for FIRE_RESULT (OpFireResult).
const (
FireResultMiss = 1 // miss (промах)
FireResultHit = 2 // hit (простое попадание)
FireResultSunk = 3 // ship sunk (корабль потоплен)
FireResultInvalidPosition = 4 // invalid position (неверная позиция)
)
// Match end codes.
const (
EndCodeJoinTimeout = -1 // the second player never joined
EndCodeVictory = 1 // you won (all opponent ships sunk)
EndCodeDefeat = 2 // you lost (all your ships sunk)
)
// WebSocket close codes used by the server (application range 4000+ and RFC 6455 codes).
@@ -275,3 +289,42 @@ func BuildPlaceShips(ships []Ship) []byte {
}
return build(v)
}
// ParseFire extracts the x and y coordinates from FIRE arguments.
func ParseFire(args []json.RawMessage) (x, y int, err error) {
if len(args) != 2 {
return 0, 0, fmt.Errorf("FIRE expects 2 arguments, got %d", len(args))
}
if err := json.Unmarshal(args[0], &x); err != nil {
return 0, 0, fmt.Errorf("x coordinate is not an integer: %w", err)
}
if err := json.Unmarshal(args[1], &y); err != nil {
return 0, 0, fmt.Errorf("y coordinate is not an integer: %w", err)
}
return x, y, nil
}
// BuildFire builds a FIRE message: [6, x, y].
func BuildFire(x, y int) []byte {
return build([]any{OpFire, x, y})
}
// ParseFireResult extracts seq and result code from FIRE_RESULT arguments.
func ParseFireResult(args []json.RawMessage) (seq int64, result int, err error) {
if len(args) != 2 {
return 0, 0, fmt.Errorf("FIRE_RESULT expects 2 arguments, got %d", len(args))
}
if err := json.Unmarshal(args[0], &seq); err != nil {
return 0, 0, fmt.Errorf("seq is not a number: %w", err)
}
if err := json.Unmarshal(args[1], &result); err != nil {
return 0, 0, fmt.Errorf("result is not a number: %w", err)
}
return seq, result, nil
}
// BuildFireResult builds a FIRE_RESULT message: [7, seq, result].
func BuildFireResult(seq int64, result int) []byte {
return build([]any{OpFireResult, seq, result})
}