Skip to content

Commit bfa3243

Browse files
committed
Phase3: Refactor fire big method and minor type fixes
Refactored a ShotResult for an error message
1 parent 8ebc7dd commit bfa3243

File tree

3 files changed

+60
-53
lines changed

3 files changed

+60
-53
lines changed

test-pnpm/battleships/constants.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,8 @@
1+
import { ShotResult } from './game.types';
2+
13
export const BLANK_BOARD_CELL = ' ';
24
export const DEFAULT_BOARD_SIZE = 10;
5+
export const NO_HIT = {
6+
hit: false,
7+
message: 'Miss!',
8+
} as ShotResult;

test-pnpm/battleships/game.service.should.test.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -211,9 +211,7 @@ describe('GameService should', () => {
211211
]);
212212

213213
gameService.fire('Player1', { x: 2, y: 2 });
214-
const result = gameService.fire('Player1', { x: 2, y: 2 });
215-
expect(result.hit).toBe(false);
216-
expect(result.message).toBe('Already fired at (2, 2)');
214+
expect(() => gameService.fire('Player1', { x: 2, y: 2 })).toThrow('Already fired at (2, 2)');
217215
});
218216

219217
test('throw error when firing out of bounds', () => {

test-pnpm/battleships/game.service.ts

Lines changed: 53 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { DEFAULT_BOARD_SIZE } from './constants';
1+
import { DEFAULT_BOARD_SIZE, NO_HIT } from './constants';
22
import { Ship, Coordinate, CellState, ShotResult, GameState, Game } from './game.types';
33
import { EOL } from 'os';
44

@@ -69,48 +69,12 @@ export class GameService implements IGameService {
6969
this.ensurePlayerExists(playerName);
7070
this.ensureWithinBoard(target);
7171
this.ensurePlayerTurn(playerName);
72-
73-
const gameState = this._game.states.get(playerName);
74-
if (!gameState) {
75-
throw new Error(`No board found for player: ${playerName}`);
76-
}
77-
78-
const currentState = gameState.board[target.y][target.x];
79-
if (currentState === CellState.Hit || currentState === CellState.Miss) {
80-
return {
81-
hit: false,
82-
message: `Already fired at (${target.x}, ${target.y})`,
83-
};
84-
}
85-
72+
const game = this.fetchGame(playerName);
73+
const currentState = game.board[target.y][target.x];
74+
this.ensureCellNotAlreadyUsed(currentState, target);
8675
const hit = currentState !== CellState.Empty;
87-
gameState.board[target.y][target.x] = hit ? CellState.Hit : CellState.Miss;
88-
89-
if (hit) {
90-
const coordString = `${target.x},${target.y}`;
91-
const ship = this.findShipAtCoordinate(gameState.ships, target);
92-
if (ship) {
93-
ship.hits.add(coordString);
94-
const shipDestroyed = this.isShipDestroyed(ship);
95-
const gameWon = this.checkForWin(gameState.ships);
96-
97-
if (gameWon) {
98-
gameState.winner = playerName;
99-
}
100-
101-
return {
102-
hit: true,
103-
message: shipDestroyed ? 'Ship destroyed!' : 'Hit!',
104-
shipDestroyed,
105-
gameWon,
106-
};
107-
}
108-
}
109-
110-
return {
111-
hit: false,
112-
message: 'Miss!',
113-
};
76+
game.board[target.y][target.x] = hit ? CellState.Hit : CellState.Miss;
77+
return hit ? this.getShotResult(target, game, playerName) : NO_HIT;
11478
}
11579

11680
getWinner(playerName: string): string | null {
@@ -150,26 +114,65 @@ export class GameService implements IGameService {
150114
return lines.join(EOL);
151115
}
152116

153-
private ensurePlayerTurn(playerName: string) {
117+
private ensureCellNotAlreadyUsed(currentState: string, target: Coordinate): void {
118+
if (this.isCellHitOrMissed(currentState)) {
119+
throw new Error(`Already fired at (${target.x}, ${target.y})`);
120+
}
121+
}
122+
123+
private getShotResult(target: Coordinate, game: GameState, playerName: string): ShotResult {
124+
const ship = this.findShipAtCoordinate(game.ships, target);
125+
ship.hits.add(`${target.x},${target.y}`);
126+
const shipDestroyed = this.isShipDestroyed(ship);
127+
const isWinner = this.checkForWin(game.ships);
128+
if (isWinner) {
129+
game.winner = playerName;
130+
}
131+
132+
return {
133+
hit: true,
134+
message: shipDestroyed ? 'Ship destroyed!' : 'Hit!',
135+
shipDestroyed,
136+
gameWon: isWinner,
137+
};
138+
}
139+
140+
private isCellHitOrMissed(currentState: string): boolean {
141+
return currentState === CellState.Hit || currentState === CellState.Miss;
142+
}
143+
144+
private fetchGame(playerName: string): GameState {
145+
const gameState = this._game.states.get(playerName);
146+
this.ensureGameExists(gameState, playerName);
147+
return gameState;
148+
}
149+
150+
private ensureGameExists(gameState: GameState, playerName: string): void {
151+
if (!gameState) {
152+
throw new Error(`No board found for player: ${playerName}`);
153+
}
154+
}
155+
156+
private ensurePlayerTurn(playerName: string): void {
154157
const currentPlayer = this.getCurrentPlayer();
155158
if (currentPlayer !== playerName) {
156159
throw new Error(`Not your turn. Current player: ${currentPlayer}`);
157160
}
158161
}
159162

160-
private ensureNoShipOverlap(board: string[][], coord: Coordinate) {
163+
private ensureNoShipOverlap(board: string[][], coord: Coordinate): void {
161164
if (this.checkForShipOverlap(board, coord)) {
162165
throw new Error(`Ship overlap at (${coord.x}, ${coord.y})`);
163166
}
164167
}
165168

166-
private ensureWithinBoard(coord: Coordinate) {
167-
if (this.outsideBoard(coord)) {
169+
private ensureWithinBoard(coord: Coordinate): void {
170+
if (this.isOutsideBoard(coord)) {
168171
throw new Error(`Ship placement out of bounds: (${coord.x}, ${coord.y})`);
169172
}
170173
}
171174

172-
private ensurePlayerExists(playerName: string) {
175+
private ensurePlayerExists(playerName: string): void {
173176
if (!this.hasPlayer(playerName)) {
174177
throw new Error(`Unknown player: ${playerName}`);
175178
}
@@ -199,11 +202,11 @@ export class GameService implements IGameService {
199202
return board[coord.y][coord.x] !== CellState.Empty;
200203
}
201204

202-
private outsideBoard(coord: Coordinate) {
205+
private isOutsideBoard(coord: Coordinate): boolean {
203206
return coord.x < 0 || coord.x >= this._boardSize || coord.y < 0 || coord.y >= this._boardSize;
204207
}
205208

206-
private printContent(board: string[][], lines: string[]) {
209+
private printContent(board: string[][], lines: string[]): void {
207210
for (let y = 0; y < this._boardSize; y++) {
208211
let row = ` ${y}|`;
209212
for (let x = 0; x < this._boardSize; x++) {
@@ -213,7 +216,7 @@ export class GameService implements IGameService {
213216
}
214217
}
215218

216-
private printHeader(lines: string[]) {
219+
private printHeader(lines: string[]): void {
217220
let header = ' |';
218221
for (let x = 0; x < this._boardSize; x++) {
219222
header += ` ${x} |`;

0 commit comments

Comments
 (0)