From 3940de1e5a5322daecc82e0aad2424daeff7dbc0 Mon Sep 17 00:00:00 2001 From: guillaume Date: Mon, 20 Nov 2023 00:53:35 +0100 Subject: [PATCH 01/11] hole 3 --- src/12_RefactoringGolf/hole1/README.md | 22 +++++ src/12_RefactoringGolf/hole1/kata.ts | 130 +++++++++++++++---------- src/12_RefactoringGolf/hole1/test.ts | 16 ++- src/README.md | 22 ----- 4 files changed, 111 insertions(+), 79 deletions(-) create mode 100644 src/12_RefactoringGolf/hole1/README.md delete mode 100644 src/README.md diff --git a/src/12_RefactoringGolf/hole1/README.md b/src/12_RefactoringGolf/hole1/README.md new file mode 100644 index 0000000..e5237ca --- /dev/null +++ b/src/12_RefactoringGolf/hole1/README.md @@ -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 diff --git a/src/12_RefactoringGolf/hole1/kata.ts b/src/12_RefactoringGolf/hole1/kata.ts index c750e5e..0fadad1 100644 --- a/src/12_RefactoringGolf/hole1/kata.ts +++ b/src/12_RefactoringGolf/hole1/kata.ts @@ -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 { @@ -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; } } diff --git a/src/12_RefactoringGolf/hole1/test.ts b/src/12_RefactoringGolf/hole1/test.ts index 44d6c2a..9aa2e2e 100644 --- a/src/12_RefactoringGolf/hole1/test.ts +++ b/src/12_RefactoringGolf/hole1/test.ts @@ -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', () => { diff --git a/src/README.md b/src/README.md deleted file mode 100644 index 4cfc9a0..0000000 --- a/src/README.md +++ /dev/null @@ -1,22 +0,0 @@ -# Hole 1 to Hole 2 - -Change the code in hole 1 to be identical to the code in hole 2; implementation and tests can change. - -## Refactorings - -- Tackle code comments, long method and large class - - Extract method - -## 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; no failing tests - - 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 compilation errors or test fails, use `git reset` to go back to green From e32a3ea105bfde43287b26c3453fcdac628b652d Mon Sep 17 00:00:00 2001 From: guillaume Date: Mon, 20 Nov 2023 16:54:21 +0100 Subject: [PATCH 02/11] enable linter --- src/12_RefactoringGolf/hole1/kata.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/12_RefactoringGolf/hole1/kata.ts b/src/12_RefactoringGolf/hole1/kata.ts index 0fadad1..050dc06 100644 --- a/src/12_RefactoringGolf/hole1/kata.ts +++ b/src/12_RefactoringGolf/hole1/kata.ts @@ -1,7 +1,7 @@ -/* eslint-disable */ +const leBonNom = ' '; export class Game { - private _lastSymbol = ' '; + private _lastSymbol = leBonNom; private _board: Board = new Board(); public Play(symbol: string, x: number, y: number): void { From 31e1f20ab50dc2d1185b1b940f3399ec831e704a Mon Sep 17 00:00:00 2001 From: guillaume iter Date: Mon, 27 Nov 2023 13:12:34 +0100 Subject: [PATCH 03/11] Tackle scope of constants and variables --- src/12_RefactoringGolf/hole1/README.md | 16 +++++-- src/12_RefactoringGolf/hole1/kata.ts | 64 ++++++++++++++++---------- 2 files changed, 52 insertions(+), 28 deletions(-) diff --git a/src/12_RefactoringGolf/hole1/README.md b/src/12_RefactoringGolf/hole1/README.md index e5237ca..99989af 100644 --- a/src/12_RefactoringGolf/hole1/README.md +++ b/src/12_RefactoringGolf/hole1/README.md @@ -1,15 +1,23 @@ -# Hole 3 to Hole 4 +# Hole 4 to Hole 5 -Change the code in hole 3 to be identical to the code on hole 4, both implenentation and tests can change. +Change the code in hole 4 to be identical to the code on hole 5, both implenentation and tests can change. ## Refactorings -- Remove magic strings and numbers - - Introduce constant +- 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 diff --git a/src/12_RefactoringGolf/hole1/kata.ts b/src/12_RefactoringGolf/hole1/kata.ts index 050dc06..4e9ba71 100644 --- a/src/12_RefactoringGolf/hole1/kata.ts +++ b/src/12_RefactoringGolf/hole1/kata.ts @@ -1,9 +1,19 @@ +/* eslint-disable */ -const leBonNom = ' '; export class Game { - private _lastSymbol = leBonNom; + private _lastSymbol = ' '; private _board: Board = new Board(); + private readonly playerO = 'O'; + private readonly emptyPlay = ' '; + + private readonly firstRow = 0; + private readonly secondRow = 1; + private readonly thirdRow = 2; + private readonly firstColumn = 0; + private readonly secondColumn = 1; + private readonly thirdColumn = 2; + public Play(symbol: string, x: number, y: number): void { this.validateFirstMove(symbol); this.validatePlayer(symbol); @@ -14,8 +24,8 @@ export class Game { } private validateFirstMove(player: string) { - if (this._lastSymbol == ' ') { - if (player == 'O') { + if (this._lastSymbol == this.emptyPlay) { + if (player == this.playerO) { throw new Error('Invalid first player'); } } @@ -28,7 +38,7 @@ export class Game { } private validatePositionIsEmpty(x: number, y: number) { - if (this._board.TileAt(x, y).Symbol != ' ') { + if (this._board.TileAt(x, y).Symbol != this.emptyPlay) { throw new Error('Invalid position'); } } @@ -43,62 +53,68 @@ export class Game { public Winner(): string { if (this.isFirstRowFull() && this.isFirstRowFullWithSameSymbol()) { - return this._board.TileAt(0, 0)!.Symbol; + return this._board.TileAt(this.firstRow, this.firstColumn)!.Symbol; } if (this.isSecondRowFull() && this.isSecondRowFullWithSameSymbol()) { - return this._board.TileAt(1, 0)!.Symbol; + return this._board.TileAt(this.secondRow, this.firstColumn)!.Symbol; } if (this.isThirdRowFull() && this.isThirdRowFullWithSameSymbol()) { - return this._board.TileAt(2, 0)!.Symbol; + return this._board.TileAt(this.thirdRow, this.firstColumn)!.Symbol; } - return ' '; + return this.emptyPlay; } private isFirstRowFull() { return ( - this._board.TileAt(0, 0)!.Symbol != ' ' && - this._board.TileAt(0, 1)!.Symbol != ' ' && - this._board.TileAt(0, 2)!.Symbol != ' ' + this._board.TileAt(this.firstRow, this.firstColumn)!.Symbol != this.emptyPlay && + this._board.TileAt(this.firstRow, this.secondColumn)!.Symbol != this.emptyPlay && + this._board.TileAt(this.firstRow, this.thirdColumn)!.Symbol != this.emptyPlay ); } 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 + this._board.TileAt(this.firstRow, this.firstColumn)!.Symbol == + this._board.TileAt(this.firstRow, this.secondColumn)!.Symbol && + this._board.TileAt(this.firstRow, this.thirdColumn)!.Symbol == + this._board.TileAt(this.firstRow, this.secondColumn)!.Symbol ); } private isSecondRowFull() { return ( - this._board.TileAt(1, 0)!.Symbol != ' ' && - this._board.TileAt(1, 1)!.Symbol != ' ' && - this._board.TileAt(1, 2)!.Symbol != ' ' + this._board.TileAt(this.secondRow, this.firstColumn)!.Symbol != this.emptyPlay && + this._board.TileAt(this.secondRow, this.secondColumn)!.Symbol != this.emptyPlay && + this._board.TileAt(this.secondRow, this.thirdColumn)!.Symbol != this.emptyPlay ); } 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 + this._board.TileAt(this.secondRow, this.firstColumn)!.Symbol == + this._board.TileAt(this.secondRow, this.secondColumn)!.Symbol && + this._board.TileAt(this.secondRow, this.thirdColumn)!.Symbol == + this._board.TileAt(this.secondRow, this.secondColumn)!.Symbol ); } private isThirdRowFull() { return ( - this._board.TileAt(2, 0)!.Symbol != ' ' && - this._board.TileAt(2, 1)!.Symbol != ' ' && - this._board.TileAt(2, 2)!.Symbol != ' ' + this._board.TileAt(this.thirdRow, this.firstColumn)!.Symbol != this.emptyPlay && + this._board.TileAt(this.thirdRow, this.secondColumn)!.Symbol != this.emptyPlay && + this._board.TileAt(this.thirdRow, this.thirdColumn)!.Symbol != this.emptyPlay ); } 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 + this._board.TileAt(this.thirdRow, this.firstColumn)!.Symbol == + this._board.TileAt(this.thirdRow, this.secondColumn)!.Symbol && + this._board.TileAt(this.thirdRow, this.thirdColumn)!.Symbol == + this._board.TileAt(this.thirdRow, this.secondColumn)!.Symbol ); } } From 39ab032c9c13ebcd78756f89c274ad42dc6d1672 Mon Sep 17 00:00:00 2001 From: guillaume iter Date: Mon, 27 Nov 2023 13:34:59 +0100 Subject: [PATCH 04/11] Tackle feature envy --- src/12_RefactoringGolf/hole1/README.md | 16 ++--- src/12_RefactoringGolf/hole1/kata.ts | 99 +++++++++----------------- 2 files changed, 36 insertions(+), 79 deletions(-) diff --git a/src/12_RefactoringGolf/hole1/README.md b/src/12_RefactoringGolf/hole1/README.md index 99989af..f744b73 100644 --- a/src/12_RefactoringGolf/hole1/README.md +++ b/src/12_RefactoringGolf/hole1/README.md @@ -1,23 +1,15 @@ -# Hole 4 to Hole 5 +# Hole 5 to Hole 6 -Change the code in hole 4 to be identical to the code on hole 5, both implenentation and tests can change. +Change the code in hole 5 to be identical to the code on hole 6, 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. +- Tackle feature envy + - Move method ## 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 diff --git a/src/12_RefactoringGolf/hole1/kata.ts b/src/12_RefactoringGolf/hole1/kata.ts index 4e9ba71..78aa8a0 100644 --- a/src/12_RefactoringGolf/hole1/kata.ts +++ b/src/12_RefactoringGolf/hole1/kata.ts @@ -1,18 +1,18 @@ /* eslint-disable */ -export class Game { - private _lastSymbol = ' '; - private _board: Board = new Board(); +const firstRow = 0; +const secondRow = 1; +const thirdRow = 2; +const firstColumn = 0; +const secondColumn = 1; +const thirdColumn = 2; - private readonly playerO = 'O'; - private readonly emptyPlay = ' '; +const playerO = 'O'; +const emptyPlay = ' '; - private readonly firstRow = 0; - private readonly secondRow = 1; - private readonly thirdRow = 2; - private readonly firstColumn = 0; - private readonly secondColumn = 1; - private readonly thirdColumn = 2; +export class Game { + private _lastSymbol = emptyPlay; + private _board: Board = new Board(); public Play(symbol: string, x: number, y: number): void { this.validateFirstMove(symbol); @@ -24,8 +24,8 @@ export class Game { } private validateFirstMove(player: string) { - if (this._lastSymbol == this.emptyPlay) { - if (player == this.playerO) { + if (this._lastSymbol == emptyPlay) { + if (player == playerO) { throw new Error('Invalid first player'); } } @@ -38,7 +38,7 @@ export class Game { } private validatePositionIsEmpty(x: number, y: number) { - if (this._board.TileAt(x, y).Symbol != this.emptyPlay) { + if (this._board.TileAt(x, y).Symbol != emptyPlay) { throw new Error('Invalid position'); } } @@ -52,69 +52,34 @@ export class Game { } public Winner(): string { - if (this.isFirstRowFull() && this.isFirstRowFullWithSameSymbol()) { - return this._board.TileAt(this.firstRow, this.firstColumn)!.Symbol; + if (this.isRowFull(firstRow) && this.isRowFullWithSameSymbol(firstRow)) { + return this._board.TileAt(firstRow, firstColumn)!.Symbol; } - if (this.isSecondRowFull() && this.isSecondRowFullWithSameSymbol()) { - return this._board.TileAt(this.secondRow, this.firstColumn)!.Symbol; + if (this.isRowFull(secondRow) && this.isRowFullWithSameSymbol(secondRow)) { + return this._board.TileAt(secondRow, firstColumn)!.Symbol; } - if (this.isThirdRowFull() && this.isThirdRowFullWithSameSymbol()) { - return this._board.TileAt(this.thirdRow, this.firstColumn)!.Symbol; + if (this.isRowFull(thirdRow) && this.isRowFullWithSameSymbol(thirdRow)) { + return this._board.TileAt(thirdRow, firstColumn)!.Symbol; } - return this.emptyPlay; - } - - private isFirstRowFull() { - return ( - this._board.TileAt(this.firstRow, this.firstColumn)!.Symbol != this.emptyPlay && - this._board.TileAt(this.firstRow, this.secondColumn)!.Symbol != this.emptyPlay && - this._board.TileAt(this.firstRow, this.thirdColumn)!.Symbol != this.emptyPlay - ); - } - - private isFirstRowFullWithSameSymbol() { - return ( - this._board.TileAt(this.firstRow, this.firstColumn)!.Symbol == - this._board.TileAt(this.firstRow, this.secondColumn)!.Symbol && - this._board.TileAt(this.firstRow, this.thirdColumn)!.Symbol == - this._board.TileAt(this.firstRow, this.secondColumn)!.Symbol - ); - } - - private isSecondRowFull() { - return ( - this._board.TileAt(this.secondRow, this.firstColumn)!.Symbol != this.emptyPlay && - this._board.TileAt(this.secondRow, this.secondColumn)!.Symbol != this.emptyPlay && - this._board.TileAt(this.secondRow, this.thirdColumn)!.Symbol != this.emptyPlay - ); - } - - private isSecondRowFullWithSameSymbol() { - return ( - this._board.TileAt(this.secondRow, this.firstColumn)!.Symbol == - this._board.TileAt(this.secondRow, this.secondColumn)!.Symbol && - this._board.TileAt(this.secondRow, this.thirdColumn)!.Symbol == - this._board.TileAt(this.secondRow, this.secondColumn)!.Symbol - ); + return emptyPlay; } - private isThirdRowFull() { + private isRowFull(row: number) { return ( - this._board.TileAt(this.thirdRow, this.firstColumn)!.Symbol != this.emptyPlay && - this._board.TileAt(this.thirdRow, this.secondColumn)!.Symbol != this.emptyPlay && - this._board.TileAt(this.thirdRow, this.thirdColumn)!.Symbol != this.emptyPlay + this._board.TileAt(row, firstColumn)!.Symbol != emptyPlay && + this._board.TileAt(row, secondColumn)!.Symbol != emptyPlay && + this._board.TileAt(row, thirdColumn)!.Symbol != emptyPlay ); } - private isThirdRowFullWithSameSymbol() { + private isRowFullWithSameSymbol(row: number) { return ( - this._board.TileAt(this.thirdRow, this.firstColumn)!.Symbol == - this._board.TileAt(this.thirdRow, this.secondColumn)!.Symbol && - this._board.TileAt(this.thirdRow, this.thirdColumn)!.Symbol == - this._board.TileAt(this.thirdRow, this.secondColumn)!.Symbol + this._board.TileAt(row, firstColumn)!.Symbol == + this._board.TileAt(row, secondColumn)!.Symbol && + this._board.TileAt(row, thirdColumn)!.Symbol == this._board.TileAt(row, secondColumn)!.Symbol ); } } @@ -129,9 +94,9 @@ class Board { private _plays: Tile[] = []; constructor() { - for (let i = 0; i < 3; i++) { - for (let j = 0; j < 3; j++) { - const tile: Tile = { X: i, Y: j, Symbol: ' ' }; + for (let i = firstRow; i <= thirdRow; i++) { + for (let j = firstColumn; j <= thirdColumn; j++) { + const tile: Tile = { X: i, Y: j, Symbol: emptyPlay }; this._plays.push(tile); } } From 0586f5fc8c4c08764716ee6434c69f09e7f20f94 Mon Sep 17 00:00:00 2001 From: guillaume iter Date: Mon, 27 Nov 2023 13:36:39 +0100 Subject: [PATCH 05/11] Tackle data class --- src/12_RefactoringGolf/hole1/README.md | 8 ++-- src/12_RefactoringGolf/hole1/kata.ts | 61 ++++++++++++++------------ src/12_RefactoringGolf/hole1/test.ts | 2 +- 3 files changed, 37 insertions(+), 34 deletions(-) diff --git a/src/12_RefactoringGolf/hole1/README.md b/src/12_RefactoringGolf/hole1/README.md index f744b73..41a346f 100644 --- a/src/12_RefactoringGolf/hole1/README.md +++ b/src/12_RefactoringGolf/hole1/README.md @@ -1,11 +1,11 @@ -# Hole 5 to Hole 6 +# Hole 6 to Hole 7 -Change the code in hole 5 to be identical to the code on hole 6, both implenentation and tests can change. +Change the code in hole 6 to be identical to the code on hole 7, both implenentation and tests can change. ## Refactorings -- Tackle feature envy - - Move method +- Tackle data class + - Move behaviour from classes to data class ## Tips diff --git a/src/12_RefactoringGolf/hole1/kata.ts b/src/12_RefactoringGolf/hole1/kata.ts index 78aa8a0..81fbb83 100644 --- a/src/12_RefactoringGolf/hole1/kata.ts +++ b/src/12_RefactoringGolf/hole1/kata.ts @@ -52,35 +52,7 @@ export class Game { } public Winner(): string { - if (this.isRowFull(firstRow) && this.isRowFullWithSameSymbol(firstRow)) { - return this._board.TileAt(firstRow, firstColumn)!.Symbol; - } - - if (this.isRowFull(secondRow) && this.isRowFullWithSameSymbol(secondRow)) { - return this._board.TileAt(secondRow, firstColumn)!.Symbol; - } - - if (this.isRowFull(thirdRow) && this.isRowFullWithSameSymbol(thirdRow)) { - return this._board.TileAt(thirdRow, firstColumn)!.Symbol; - } - - 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 - ); + return this._board.findRowFullWithSamePlayer(); } } @@ -109,4 +81,35 @@ class Board { public AddTileAt(symbol: string, x: number, y: number): void { this._plays.find((t: Tile) => t.X == x && t.Y == y)!.Symbol = symbol; } + + public findRowFullWithSamePlayer(): string { + if (this.isRowFull(firstRow) && this.isRowFullWithSameSymbol(firstRow)) { + return this.TileAt(firstRow, firstColumn)!.Symbol; + } + + if (this.isRowFull(secondRow) && this.isRowFullWithSameSymbol(secondRow)) { + return this.TileAt(secondRow, firstColumn)!.Symbol; + } + + if (this.isRowFull(thirdRow) && this.isRowFullWithSameSymbol(thirdRow)) { + return this.TileAt(thirdRow, firstColumn)!.Symbol; + } + + return emptyPlay; + } + + private isRowFull(row: number) { + return ( + this.TileAt(row, firstColumn)!.Symbol != emptyPlay && + this.TileAt(row, secondColumn)!.Symbol != emptyPlay && + this.TileAt(row, thirdColumn)!.Symbol != emptyPlay + ); + } + + private isRowFullWithSameSymbol(row: number) { + return ( + this.TileAt(row, firstColumn)!.Symbol == this.TileAt(row, secondColumn)!.Symbol && + this.TileAt(row, thirdColumn)!.Symbol == this.TileAt(row, secondColumn)!.Symbol + ); + } } diff --git a/src/12_RefactoringGolf/hole1/test.ts b/src/12_RefactoringGolf/hole1/test.ts index 9aa2e2e..9ff7927 100644 --- a/src/12_RefactoringGolf/hole1/test.ts +++ b/src/12_RefactoringGolf/hole1/test.ts @@ -1,4 +1,4 @@ -import { Game } from './kata'; +import { Game } from '../hole8/kata'; describe('TicTacToe game', () => { let game: Game; From 52ec45c5c98d9b9efb76f37a368d6de52de5d40e Mon Sep 17 00:00:00 2001 From: guillaume iter Date: Mon, 27 Nov 2023 13:37:59 +0100 Subject: [PATCH 06/11] fix test --- src/12_RefactoringGolf/hole1/test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/12_RefactoringGolf/hole1/test.ts b/src/12_RefactoringGolf/hole1/test.ts index 9ff7927..9aa2e2e 100644 --- a/src/12_RefactoringGolf/hole1/test.ts +++ b/src/12_RefactoringGolf/hole1/test.ts @@ -1,4 +1,4 @@ -import { Game } from '../hole8/kata'; +import { Game } from './kata'; describe('TicTacToe game', () => { let game: Game; From 9c4ff9180ed31c403e9d81d80b20d0e1c3f015bf Mon Sep 17 00:00:00 2001 From: guillaume iter Date: Mon, 27 Nov 2023 14:29:07 +0100 Subject: [PATCH 07/11] =?UTF-8?q?hole=206:=20faites=20=C3=A9merger=20le=20?= =?UTF-8?q?domaine?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/12_RefactoringGolf/hole1/README.md | 8 ++-- src/12_RefactoringGolf/hole1/kata.ts | 59 +++++++++++++++++++------- src/12_RefactoringGolf/hole1/test.ts | 2 +- 3 files changed, 48 insertions(+), 21 deletions(-) diff --git a/src/12_RefactoringGolf/hole1/README.md b/src/12_RefactoringGolf/hole1/README.md index 41a346f..5aca0c0 100644 --- a/src/12_RefactoringGolf/hole1/README.md +++ b/src/12_RefactoringGolf/hole1/README.md @@ -1,11 +1,11 @@ -# Hole 6 to Hole 7 +# Hole 7 to Hole 8 -Change the code in hole 6 to be identical to the code on hole 7, both implenentation and tests can change. +Change the code in hole 7 to be identical to the code on hole 8, both implenentation and tests can change. ## Refactorings -- Tackle data class - - Move behaviour from classes to data class +- Tackle domain language as result of new abstractions + - Rename constants, variables, arguments, methods to better express domain language ## Tips diff --git a/src/12_RefactoringGolf/hole1/kata.ts b/src/12_RefactoringGolf/hole1/kata.ts index 81fbb83..78ee1d0 100644 --- a/src/12_RefactoringGolf/hole1/kata.ts +++ b/src/12_RefactoringGolf/hole1/kata.ts @@ -38,7 +38,7 @@ export class Game { } private validatePositionIsEmpty(x: number, y: number) { - if (this._board.TileAt(x, y).Symbol != emptyPlay) { + if (this._board.TileAt(x, y).isNotEmpty) { throw new Error('Invalid position'); } } @@ -56,30 +56,57 @@ export class Game { } } -interface Tile { - X: number; - Y: number; - Symbol: string; +class Tile { + private x: number = 0; + private y: number = 0; + private symbol: string = ' '; + + constructor(x: number, y: number, symbol: string) { + this.x = x; + this.y = y; + this.symbol = symbol; + } + + get Symbol() { + return this.symbol; + } + + get isNotEmpty() { + return this.Symbol !== emptyPlay; + } + + hasSameSymbolAs(other: Tile) { + return this.Symbol === other.Symbol; + } + + hasSameCoordinatesAs(other: Tile) { + return this.x == other.x && this.y == other.y; + } + + updateSymbol(newSymbol: string) { + this.symbol = newSymbol; + } } class Board { private _plays: Tile[] = []; constructor() { - for (let i = firstRow; i <= thirdRow; i++) { - for (let j = firstColumn; j <= thirdColumn; j++) { - const tile: Tile = { X: i, Y: j, Symbol: emptyPlay }; - this._plays.push(tile); + for (let x = firstRow; x <= thirdRow; x++) { + for (let y = firstColumn; y <= thirdColumn; y++) { + this._plays.push(new Tile(x, y, emptyPlay)); } } } public TileAt(x: number, y: number): Tile { - return this._plays.find((t: Tile) => t.X == x && t.Y == y)!; + return this._plays.find((t: Tile) => t.hasSameCoordinatesAs(new Tile(x, y, emptyPlay)))!; } public AddTileAt(symbol: string, x: number, y: number): void { - this._plays.find((t: Tile) => t.X == x && t.Y == y)!.Symbol = symbol; + this._plays + .find((t: Tile) => t.hasSameCoordinatesAs(new Tile(x, y, symbol)))! + .updateSymbol(symbol); } public findRowFullWithSamePlayer(): string { @@ -100,16 +127,16 @@ class Board { private isRowFull(row: number) { return ( - this.TileAt(row, firstColumn)!.Symbol != emptyPlay && - this.TileAt(row, secondColumn)!.Symbol != emptyPlay && - this.TileAt(row, thirdColumn)!.Symbol != emptyPlay + this.TileAt(row, firstColumn)!.isNotEmpty && + this.TileAt(row, secondColumn)!.isNotEmpty && + this.TileAt(row, thirdColumn)!.isNotEmpty ); } private isRowFullWithSameSymbol(row: number) { return ( - this.TileAt(row, firstColumn)!.Symbol == this.TileAt(row, secondColumn)!.Symbol && - this.TileAt(row, thirdColumn)!.Symbol == this.TileAt(row, secondColumn)!.Symbol + this.TileAt(row, firstColumn)!.hasSameSymbolAs(this.TileAt(row, secondColumn)!) && + this.TileAt(row, thirdColumn)!.hasSameSymbolAs(this.TileAt(row, secondColumn)!) ); } } diff --git a/src/12_RefactoringGolf/hole1/test.ts b/src/12_RefactoringGolf/hole1/test.ts index 9aa2e2e..457523c 100644 --- a/src/12_RefactoringGolf/hole1/test.ts +++ b/src/12_RefactoringGolf/hole1/test.ts @@ -1,4 +1,4 @@ -import { Game } from './kata'; +import { Game } from '../hole6/kata'; describe('TicTacToe game', () => { let game: Game; From c76c7aa430e3653e0d1985c88fd034eb6944652e Mon Sep 17 00:00:00 2001 From: guillaume iter Date: Mon, 27 Nov 2023 14:48:12 +0100 Subject: [PATCH 08/11] fix test --- src/12_RefactoringGolf/hole1/test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/12_RefactoringGolf/hole1/test.ts b/src/12_RefactoringGolf/hole1/test.ts index 457523c..9aa2e2e 100644 --- a/src/12_RefactoringGolf/hole1/test.ts +++ b/src/12_RefactoringGolf/hole1/test.ts @@ -1,4 +1,4 @@ -import { Game } from '../hole6/kata'; +import { Game } from './kata'; describe('TicTacToe game', () => { let game: Game; From c1e1843d6d36f89cc9488b68203f0fbeec3d22c1 Mon Sep 17 00:00:00 2001 From: guillaume iter Date: Mon, 27 Nov 2023 16:44:36 +0100 Subject: [PATCH 09/11] fix instructions --- src/12_RefactoringGolf/hole1/README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/12_RefactoringGolf/hole1/README.md b/src/12_RefactoringGolf/hole1/README.md index 5aca0c0..c7b71e5 100644 --- a/src/12_RefactoringGolf/hole1/README.md +++ b/src/12_RefactoringGolf/hole1/README.md @@ -1,6 +1,5 @@ -# Hole 7 to Hole 8 +# Hole 6 to Hole 7 -Change the code in hole 7 to be identical to the code on hole 8, both implenentation and tests can change. ## Refactorings From 006f0e0f40dddb556276e113e00a85b0c3211607 Mon Sep 17 00:00:00 2001 From: guillaume iter Date: Mon, 27 Nov 2023 16:55:57 +0100 Subject: [PATCH 10/11] domain language --- src/12_RefactoringGolf/hole1/README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/12_RefactoringGolf/hole1/README.md b/src/12_RefactoringGolf/hole1/README.md index c7b71e5..c10f072 100644 --- a/src/12_RefactoringGolf/hole1/README.md +++ b/src/12_RefactoringGolf/hole1/README.md @@ -6,6 +6,13 @@ - Tackle domain language as result of new abstractions - Rename constants, variables, arguments, methods to better express domain language +Le code parle du Jeu TIC TAC TOE, on ne devrait employer des termes qui ont du sens dans ce contexte là +https://en.wikipedia.org/wiki/Tic-tac-toe + +Tic-tac-toe is played on a three-by-three grid by two players, who alternately place the marks X and O in one of the nine spaces in the grid. + +The player who succeeds in placing three of their marks in a horizontal, vertical, or diagonal row is the winner. + ## Tips - Use a diff tool to identify the code changes you need to perform From b19f6966fd50e6d7da78baaf43ced4296ed138eb Mon Sep 17 00:00:00 2001 From: Jules Cayrol Date: Thu, 30 Nov 2023 16:47:45 +0100 Subject: [PATCH 11/11] Remplacement de tout ce qui contient le mot "symbol" par le mot "player" + Remplacement de emptyPlay par noPlayer --- src/12_RefactoringGolf/hole1/kata.ts | 70 ++++++++++++++-------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/src/12_RefactoringGolf/hole1/kata.ts b/src/12_RefactoringGolf/hole1/kata.ts index 78ee1d0..1017ebf 100644 --- a/src/12_RefactoringGolf/hole1/kata.ts +++ b/src/12_RefactoringGolf/hole1/kata.ts @@ -8,23 +8,23 @@ const secondColumn = 1; const thirdColumn = 2; const playerO = 'O'; -const emptyPlay = ' '; +const noPlayer = ' '; export class Game { - private _lastSymbol = emptyPlay; + private _lastPlayer = noPlayer; private _board: Board = new Board(); - public Play(symbol: string, x: number, y: number): void { - this.validateFirstMove(symbol); - this.validatePlayer(symbol); + public Play(player: string, x: number, y: number): void { + this.validateFirstMove(player); + this.validatePlayer(player); this.validatePositionIsEmpty(x, y); - this.updateLastPlayer(symbol); - this.updateBoard(symbol, x, y); + this.updateLastPlayer(player); + this.updateBoard(player, x, y); } private validateFirstMove(player: string) { - if (this._lastSymbol == emptyPlay) { + if (this._lastPlayer == noPlayer) { if (player == playerO) { throw new Error('Invalid first player'); } @@ -32,7 +32,7 @@ export class Game { } private validatePlayer(player: string) { - if (player == this._lastSymbol) { + if (player == this._lastPlayer) { throw new Error('Invalid next player'); } } @@ -44,7 +44,7 @@ export class Game { } private updateLastPlayer(player: string) { - this._lastSymbol = player; + this._lastPlayer = player; } private updateBoard(player: string, x: number, y: number) { @@ -59,32 +59,32 @@ export class Game { class Tile { private x: number = 0; private y: number = 0; - private symbol: string = ' '; + private player: string = noPlayer; - constructor(x: number, y: number, symbol: string) { + constructor(x: number, y: number, player: string) { this.x = x; this.y = y; - this.symbol = symbol; + this.player = player; } - get Symbol() { - return this.symbol; + get Player() { + return this.player; } get isNotEmpty() { - return this.Symbol !== emptyPlay; + return this.player !== noPlayer; } - hasSameSymbolAs(other: Tile) { - return this.Symbol === other.Symbol; + hasSamePlayerAs(other: Tile) { + return this.player === other.player; } hasSameCoordinatesAs(other: Tile) { return this.x == other.x && this.y == other.y; } - updateSymbol(newSymbol: string) { - this.symbol = newSymbol; + updatePlayer(newPlayer: string) { + this.player = newPlayer; } } @@ -94,35 +94,35 @@ class Board { constructor() { for (let x = firstRow; x <= thirdRow; x++) { for (let y = firstColumn; y <= thirdColumn; y++) { - this._plays.push(new Tile(x, y, emptyPlay)); + this._plays.push(new Tile(x, y, noPlayer)); } } } public TileAt(x: number, y: number): Tile { - return this._plays.find((t: Tile) => t.hasSameCoordinatesAs(new Tile(x, y, emptyPlay)))!; + return this._plays.find((t: Tile) => t.hasSameCoordinatesAs(new Tile(x, y, noPlayer)))!; } - public AddTileAt(symbol: string, x: number, y: number): void { + public AddTileAt(player: string, x: number, y: number): void { this._plays - .find((t: Tile) => t.hasSameCoordinatesAs(new Tile(x, y, symbol)))! - .updateSymbol(symbol); + .find((t: Tile) => t.hasSameCoordinatesAs(new Tile(x, y, player)))! + .updatePlayer(player); } public findRowFullWithSamePlayer(): string { - if (this.isRowFull(firstRow) && this.isRowFullWithSameSymbol(firstRow)) { - return this.TileAt(firstRow, firstColumn)!.Symbol; + if (this.isRowFull(firstRow) && this.isRowFullWithSamePlayer(firstRow)) { + return this.TileAt(firstRow, firstColumn)!.Player; } - if (this.isRowFull(secondRow) && this.isRowFullWithSameSymbol(secondRow)) { - return this.TileAt(secondRow, firstColumn)!.Symbol; + if (this.isRowFull(secondRow) && this.isRowFullWithSamePlayer(secondRow)) { + return this.TileAt(secondRow, firstColumn)!.Player; } - if (this.isRowFull(thirdRow) && this.isRowFullWithSameSymbol(thirdRow)) { - return this.TileAt(thirdRow, firstColumn)!.Symbol; + if (this.isRowFull(thirdRow) && this.isRowFullWithSamePlayer(thirdRow)) { + return this.TileAt(thirdRow, firstColumn)!.Player; } - return emptyPlay; + return noPlayer; } private isRowFull(row: number) { @@ -133,10 +133,10 @@ class Board { ); } - private isRowFullWithSameSymbol(row: number) { + private isRowFullWithSamePlayer(row: number) { return ( - this.TileAt(row, firstColumn)!.hasSameSymbolAs(this.TileAt(row, secondColumn)!) && - this.TileAt(row, thirdColumn)!.hasSameSymbolAs(this.TileAt(row, secondColumn)!) + this.TileAt(row, firstColumn)!.hasSamePlayerAs(this.TileAt(row, secondColumn)!) && + this.TileAt(row, thirdColumn)!.hasSamePlayerAs(this.TileAt(row, secondColumn)!) ); } }