Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions src/12_RefactoringGolf/hole1/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Hole 4 to Hole 5

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

## Refactorings

- Tackle scope of constants and variables
- Move constants and variables to proper scope
- les constantes et les variable ne sont pas au bon endroit, dans le bon scope
- Déplacez les pour qu'elles ne soient pas globales mais utilisées aux seuls endroits où on en a besoin
-
- Tackle duplication
- Introduce more generic methods to replace duplicted methods
- il y a des méthodes qui font la meme chose, trouvez les, et en les rendant à peine plus générique, réduisez le code dupliqué au strict nécessaire.

## Tips

- Use a diff tool to identify the code changes you need to perform
- https://devconnected.com/how-to-compare-two-git-branches/
- $ git diff branch1..branch2
- 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
117 changes: 61 additions & 56 deletions src/12_RefactoringGolf/hole1/kata.ts
Original file line number Diff line number Diff line change
@@ -1,78 +1,86 @@
/* eslint-disable */

const playerO = 'O';
const emptyPlay = ' ';

const firstRow = 0;
const secondRow = 1;
const thirdRow = 2;
const firstColumn = 0;
const secondColumn = 1;
const thirdColumn = 2;

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

public Play(symbol: string, x: number, y: number): void {
//if first move
if (this._lastSymbol == ' ') {
//if player is X
if (symbol == 'O') {
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 == emptyPlay) {
if (player == playerO) {
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 != emptyPlay) {
throw new Error('Invalid position');
}
}

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

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.isRowFull(firstRow) && this.isRowFullWithSameSymbol(firstRow)) {
return this._board.TileAt(firstRow, firstColumn)!.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.isRowFull(secondRow) && this.isRowFullWithSameSymbol(secondRow)) {
return this._board.TileAt(secondRow, firstColumn)!.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.isRowFull(thirdRow) && this.isRowFullWithSameSymbol(thirdRow)) {
return this._board.TileAt(thirdRow, firstColumn)!.Symbol;
}

return ' ';
return emptyPlay;
}

private isRowFull(row: number) {
return (
this._board.TileAt(row, firstColumn)!.Symbol != emptyPlay &&
this._board.TileAt(row, secondColumn)!.Symbol != emptyPlay &&
this._board.TileAt(row, thirdColumn)!.Symbol != emptyPlay
);
}

private isRowFullWithSameSymbol(row: number) {
return (
this._board.TileAt(row, firstColumn)!.Symbol ==
this._board.TileAt(row, secondColumn)!.Symbol &&
this._board.TileAt(row, thirdColumn)!.Symbol == this._board.TileAt(row, secondColumn)!.Symbol
);
}
}

Expand All @@ -99,9 +107,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.