Skip to content

Commit

Permalink
hole 3
Browse files Browse the repository at this point in the history
  • Loading branch information
guillaume committed Nov 19, 2023
1 parent 052c77d commit 3940de1
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 79 deletions.
22 changes: 22 additions & 0 deletions src/12_RefactoringGolf/hole1/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Hole 3 to Hole 4

Change the code in hole 3 to be identical to the code on hole 4, both implenentation and tests can change.

## Refactorings

- Remove magic strings and numbers
- Introduce constant

## Tips

- Use a diff tool to identify the code changes you need to perform
- Check the code coverage is enough to detect any unintended behaviour changes

### While refactoring

- Stay in the green while refactoring
- Run the tests after each refactor
- Check all tests still pass
- Check code coverage has not dropped
- Commit after each refactor
- In case of persistent test fails, use `git reset` to go back to green
130 changes: 77 additions & 53 deletions src/12_RefactoringGolf/hole1/kata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,78 +2,105 @@

export class Game {
private _lastSymbol = ' ';
private _toto: Board = new Board();
private _board: Board = new Board();

public Play(symbol: string, x: number, y: number): void {
//if first move
this.validateFirstMove(symbol);
this.validatePlayer(symbol);
this.validatePositionIsEmpty(x, y);

this.updateLastPlayer(symbol);
this.updateBoard(symbol, x, y);
}

private validateFirstMove(player: string) {
if (this._lastSymbol == ' ') {
//if player is X
if (symbol == 'O') {
if (player == 'O') {
throw new Error('Invalid first player');
}
}
//if not first move but player repeated
else if (symbol == this._lastSymbol) {
}

private validatePlayer(player: string) {
if (player == this._lastSymbol) {
throw new Error('Invalid next player');
}
//if not first move but play on an already played tile
else if (this._toto.TileAt(x, y).Symbol != ' ') {
}

private validatePositionIsEmpty(x: number, y: number) {
if (this._board.TileAt(x, y).Symbol != ' ') {
throw new Error('Invalid position');
}
}

private updateLastPlayer(player: string) {
this._lastSymbol = player;
}

// update game state
this._lastSymbol = symbol;
this._toto.AddTileAt(symbol, x, y);
private updateBoard(player: string, x: number, y: number) {
this._board.AddTileAt(player, x, y);
}

public Winner(): string {
//if the positions in first row are taken
if (
this._toto.TileAt(0, 0)!.Symbol != ' ' &&
this._toto.TileAt(0, 1)!.Symbol != ' ' &&
this._toto.TileAt(0, 2)!.Symbol != ' '
) {
//if first row is full with same symbol
if (
this._toto.TileAt(0, 0)!.Symbol == this._toto.TileAt(0, 1)!.Symbol &&
this._toto.TileAt(0, 2)!.Symbol == this._toto.TileAt(0, 1)!.Symbol
) {
return this._toto.TileAt(0, 0)!.Symbol;
}
if (this.isFirstRowFull() && this.isFirstRowFullWithSameSymbol()) {
return this._board.TileAt(0, 0)!.Symbol;
}

//if the positions in first row are taken
if (
this._toto.TileAt(1, 0)!.Symbol != ' ' &&
this._toto.TileAt(1, 1)!.Symbol != ' ' &&
this._toto.TileAt(1, 2)!.Symbol != ' '
) {
//if middle row is full with same symbol
if (
this._toto.TileAt(1, 0)!.Symbol == this._toto.TileAt(1, 1)!.Symbol &&
this._toto.TileAt(1, 2)!.Symbol == this._toto.TileAt(1, 1)!.Symbol
) {
return this._toto.TileAt(1, 0)!.Symbol;
}
if (this.isSecondRowFull() && this.isSecondRowFullWithSameSymbol()) {
return this._board.TileAt(1, 0)!.Symbol;
}

//if the positions in first row are taken
if (
this._toto.TileAt(2, 0)!.Symbol != ' ' &&
this._toto.TileAt(2, 1)!.Symbol != ' ' &&
this._toto.TileAt(2, 2)!.Symbol != ' '
) {
//if middle row is full with same symbol
if (
this._toto.TileAt(2, 0)!.Symbol == this._toto.TileAt(2, 1)!.Symbol &&
this._toto.TileAt(2, 2)!.Symbol == this._toto.TileAt(2, 1)!.Symbol
) {
return this._toto.TileAt(2, 0)!.Symbol;
}
if (this.isThirdRowFull() && this.isThirdRowFullWithSameSymbol()) {
return this._board.TileAt(2, 0)!.Symbol;
}

return ' ';
}

private isFirstRowFull() {
return (
this._board.TileAt(0, 0)!.Symbol != ' ' &&
this._board.TileAt(0, 1)!.Symbol != ' ' &&
this._board.TileAt(0, 2)!.Symbol != ' '
);
}

private isFirstRowFullWithSameSymbol() {
return (
this._board.TileAt(0, 0)!.Symbol == this._board.TileAt(0, 1)!.Symbol &&
this._board.TileAt(0, 2)!.Symbol == this._board.TileAt(0, 1)!.Symbol
);
}

private isSecondRowFull() {
return (
this._board.TileAt(1, 0)!.Symbol != ' ' &&
this._board.TileAt(1, 1)!.Symbol != ' ' &&
this._board.TileAt(1, 2)!.Symbol != ' '
);
}

private isSecondRowFullWithSameSymbol() {
return (
this._board.TileAt(1, 0)!.Symbol == this._board.TileAt(1, 1)!.Symbol &&
this._board.TileAt(1, 2)!.Symbol == this._board.TileAt(1, 1)!.Symbol
);
}

private isThirdRowFull() {
return (
this._board.TileAt(2, 0)!.Symbol != ' ' &&
this._board.TileAt(2, 1)!.Symbol != ' ' &&
this._board.TileAt(2, 2)!.Symbol != ' '
);
}

private isThirdRowFullWithSameSymbol() {
return (
this._board.TileAt(2, 0)!.Symbol == this._board.TileAt(2, 1)!.Symbol &&
this._board.TileAt(2, 2)!.Symbol == this._board.TileAt(2, 1)!.Symbol
);
}
}

interface Tile {
Expand All @@ -99,9 +126,6 @@ class Board {
}

public AddTileAt(symbol: string, x: number, y: number): void {
//@ts-ignore
const tile: Tile = { X: x, Y: y, Symbol: symbol };

this._plays.find((t: Tile) => t.X == x && t.Y == y)!.Symbol = symbol;
}
}
16 changes: 12 additions & 4 deletions src/12_RefactoringGolf/hole1/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,31 @@ describe('TicTacToe game', () => {
});

test('should not allow player O to play first', () => {
expect(() => game.Play('O', 0, 0)).toThrow();
expect(() => {
game.Play('O', 0, 0);
}).toThrow();
});

it('should not allow player x to play twice in a row', () => {
game.Play('X', 0, 0);
expect(() => game.Play('X', 1, 0)).toThrow();
expect(() => {
game.Play('X', 1, 0);
}).toThrow();
});

it('should not allow a player to play in last played position', () => {
game.Play('X', 0, 0);
expect(() => game.Play('O', 0, 0)).toThrow();
expect(() => {
game.Play('O', 0, 0);
}).toThrow();
});

it('should not allow a player to play in any played position', () => {
game.Play('X', 0, 0);
game.Play('O', 1, 0);
expect(() => game.Play('X', 0, 0)).toThrow();
expect(() => {
game.Play('X', 0, 0);
}).toThrow();
});

it('should declare player X as winner if it plays three in top row', () => {
Expand Down
22 changes: 0 additions & 22 deletions src/README.md

This file was deleted.

0 comments on commit 3940de1

Please sign in to comment.