Commit 44fa672b authored by Gerson Sunyé's avatar Gerson Sunyé
Browse files

The README is almost finished. Added an explicative structure of new unit tests

parent b2945208
......@@ -124,17 +124,104 @@ Vous devez donc parcourir le module `move-validation` et implémenter les foncti
### Tests unitaires
Pour vérifier que les fonctions du module `move-validation` fonctionnent correctement, vous devez écrire des tests unitaires, qui vont vérifier que les fonctions acceptent les mouvements valides et n'acceptent pas les mouvements invalides.
Pour vérifier que les fonctions du module `move-validation` fonctionnent correctement, vous devez écrire des tests unitaires, qui vont vérifier que les fonctions acceptent les mouvements possibles et n'acceptent pas les mouvements impossibles.
Les mouvements sont possibles (ou impossibles) en accord avec les [règles des échecs](https://fr.wikipedia.org/wiki/Échecs).
Comme ces règles sont complexes, vous serez mené à écrire plusieurs tests unitaires pour vérifier les mouvements possibles et impossibles d'une même pièce.
Par exemple, pour tester
Les signatures des fonctions du module `move-validation` suivent la même convention:
- valider les mouvements.
- tester
```ts
function colorPieceMove(board: Chessboard, move: Move): boolean
```
Le paramètre `board` contient l'échiquier de la partie en cours et `move` contient le déplacement demandé par le joueur à travers le browser.
Le paramètre `move` contient 2 coordonnées de type `Position`, représentant le début et la fin du déplacement.
Les coordonnées indiquent **toujours** des cases à l'intérieur de l'échiquier, c'est à dire, une colonne entre `A` et `H` et une ligne entre `1` et `8`.
Donc, il n'y a pas besoin de vérifier si un déplacement conduit une pièce à l'extérieur de l'échiquier.
Les tests unitaires des fonctions `blackPawnMove()` et `whitePawnMove()` ont déjà été implémentés, vous les trouverez dans le fichier `./spec/move-validation-spec.ts`.
Ce fichier contient déjà le squelette des autres test unitaires que vous devez implémenter.
Vous devez procéder par itérations successives, n'essayez pas d'implémenter les fonctions d'un seul trait. Observez le cycle de développement suivant:
1. Implémentez une fonctionnalité simple.
2. Écrivez le ou les tests unitaires qui vérifient cette fonctionnalité.
3. Exécutez les tests pour vérifier que la fonctionnalité marche correctement et la non-régression.
4. Recommencez avec la fonctionnalité suivante.
Par exemple, lorsque vous allez implémenter les fonctions qui valident le mouvement des tours (`blackRoockMove()` et `whiteRoockMove()`) , vous pouvez subdiviser leurs comportements en différentes fonctionnalités:
- Validation des mouvements horizontaux, sans se préoccuper des autres pièces.
- Validation des mouvements verticaux, toujours sans se préoccuper des autres pièces.
- Invalidation d'des mouvements (horizontaux et verticaux) lorsque la case finale contient une pièce de même couleur.
- Validation des mouvements (horizontaux et verticaux) qui se terminent sur une case contenant une pièce d'une couleur différente.
- Invalidation des mouvements (horizontaux et verticaux) lorsque toutes les cases intermédiaires ne sont pas vides.
### Exemple: validation des mouvements d'une tour en plusieurs étapes
#### Etape 1
Commencez par la 1e fonctionnalité, la validation des déplacements horizontaux:
```ts
// Dans le fichier "move-validation.ts"
export function blackRoockMove(board: Chessboard, move: Move): boolean {
return move.from.ligne === move.to.ligne; // Si les lignes de début de fin sont les mêmes, le déplacement est horizontal
}
```
#### Pion
Écrivez ensuite le test unitaire pour cette fonctionnalité:
```ts
// Dans le fichier "move-validation-spec.ts"
describe("Test blackRoockMove()", () => {
beforeEach( () => { // Fonction exécutée avant chaque test unitaires
chessboard = createEmptyChessboard(); // Création d'un échiquier vide
});
it("A roock can move horizontally", () => {
putPiece(chessboard, positionE4, pieces.blackPawn); // Place une tour sur la case E4 d'un échiquier vide.
let moveE4-H4: Move = {from: positionE4, to: positionH4, isValid: true}; // Création d'un déplacement horizontal
expect(isPossible.blackRoockMove(chessboard, moveE4-H4)).toBeTruthy(); // Le déplacement doit être possible
});
```
#### Etape 2
Nouvelle fonctionnalité à implémenter: la validation des déplacements verticaux. Modifiez la fonction `blackRoockMove()`:
```ts
// Dans le fichier "move-validation.ts"
export function blackRoockMove(board: Chessboard, move: Move): boolean {
return move.from.line === move.to.line || // Si les lignes de début de fin sont les mêmes, le déplacement est horizontal
move.from.column === move.to.column; // Si les colonnes de début de fin sont les mêmes, le déplacement est vertical
}
```
Écrivez ensuite un nouveau test unitaire pour cette nouvelle fonctionnalité:
```ts
// Dans le fichier "move-validation-spec.ts"
describe("Test blackRoockMove()", () => {
beforeEach( () => { // Fonction exécutée avant chaque test unitaires
chessboard = createEmptyChessboard(); // Création d'un échiquier vide
});
it("A roock can move horizontally", () => { // (...)
});
it("A roock can move vertically", () => {
putPiece(chessboard, positionE4, pieces.blackPawn); // Place une tour sur la case E4 d'un échiquier vide.
let moveE4-E8: Move = {from: positionE4, to: positionE8, isValid: true}; // Création d'un déplacement vertical
expect(isPossible.blackRoockMove(chessboard, moveE4-E8)).toBeTruthy(); // Le déplacement doit être possible
});
```
#### Autres étapes
- Test1: XXX
- Test2: XXX
Suivez la même démarche pour implémenter et tester les autres fonctionnalités, c'est à dire, les autres mouvements possibles des tours.
### Rendu
......@@ -160,7 +247,13 @@ Si vous le souhaitez, vous pouvez ajouter un fichier "`RENDU.md`" à la racine d
### Derniers conseils
- Rappelez-vous que « *Une fonction sans test unitaire ne fonctionne pas* »!
- Rappelez-vous aussi que «*N'importe qui peut écrire du code compréhensible par les ordinateurs, mais seulement les bon développeurs parviennent à écrire du code intelligible par les humains* » !
- Écrivez les tests unitaires avant ou en même temps que les fonctions. Ne les laissez pas pour la fin, les test unitaires sont très utiles pendant le développement et vous feront gagner du temps.
- Parfois, les règles de déplacement sont les mêmes pour les pièces noires et blanches. Dans ces cas, vous pouvez utiliser une fonction commune.
- Faites bon usage de `git`: effectuez des *commits* et des *pushs* régulièrement ! Cela vous permet d'éviter de perdre votre travail, et de mieux collaborer en équipe.
......
import * as isValid from '../src/move-validation'
import * as isPossible from '../src/move-validation'
import * as pieces from '../src/piece'
import { Chessboard, createInitialChessboard, createEmptyChessboard, putPiece } from '../src/chessboard';
import { Chessboard, createEmptyChessboard, putPiece } from '../src/chessboard';
import { Position, position } from '../src/position';
import { Move, processMove } from '../src/movements';
import { Move } from '../src/movements';
let chessboard : Chessboard;
let positionA4 : Position = position(0, 3) // A4
......@@ -26,44 +26,44 @@ describe("Test blackPawnMove()", () => {
it("Pawns can move forward", () => {
putPiece(chessboard, positionA7, pieces.blackPawn);
let singleForward: Move = {from: positionA7, to: positionA6, isValid: true};
expect(isValid.blackPawnMove(chessboard, singleForward)).toBeTruthy();
expect(isPossible.blackPawnMove(chessboard, singleForward)).toBeTruthy();
});
it("Pawns cannot move backward", () => {
putPiece(chessboard, positionA7, pieces.blackPawn);
let singleForward: Move = {from: positionA7, to: positionA8, isValid: true};
expect(isValid.blackPawnMove(chessboard, singleForward)).toBeFalsy();
expect(isPossible.blackPawnMove(chessboard, singleForward)).toBeFalsy();
});
it("When in the initial position, paws can move 2 squares forward", () => {
putPiece(chessboard, positionA7, pieces.blackPawn);
let doubleForward: Move = {from: positionA7, to: positionA5, isValid: true};
expect(isValid.blackPawnMove(chessboard, doubleForward)).toBeTruthy();
expect(isPossible.blackPawnMove(chessboard, doubleForward)).toBeTruthy();
});
it("When a paws has already moved, it cannot move 2 squares forward", () => {
putPiece(chessboard, positionC6, pieces.blackPawn);
let doubleForward: Move = {from: positionC6, to: positionC4, isValid: true}
expect(isValid.blackPawnMove(chessboard, doubleForward)).toBeFalsy();
expect(isPossible.blackPawnMove(chessboard, doubleForward)).toBeFalsy();
});
it("When in the initial position, pawns cannot move 3 squares forward", () => {
putPiece(chessboard, positionC6, pieces.blackPawn);
let tripleForward: Move = {from: positionA7, to: positionA4, isValid: true}
expect(isValid.blackPawnMove(chessboard, tripleForward)).toBeFalsy();
expect(isPossible.blackPawnMove(chessboard, tripleForward)).toBeFalsy();
});
it("When in face of another piece, pawns cannot move foreward", () => {
putPiece(chessboard, positionA6, pieces.whitePawn);
putPiece(chessboard, positionA7, pieces.blackPawn);
let singleForward: Move = {from: positionA7, to: positionA6, isValid: true}
expect(isValid.blackPawnMove(chessboard, singleForward)).toBeFalsy();
expect(isPossible.blackPawnMove(chessboard, singleForward)).toBeFalsy();
})
it("Pawns cannot capture an empty square ", () => {
putPiece(chessboard, positionA7, pieces.blackPawn);
let diagonalCapture: Move = {from: positionA7, to: positionB6, isValid: true}
expect(isValid.blackPawnMove(chessboard, diagonalCapture)).toBeFalsy();
expect(isPossible.blackPawnMove(chessboard, diagonalCapture)).toBeFalsy();
});
it("Pawns cannot capture pieces of the same color", () => {
......@@ -71,7 +71,7 @@ describe("Test blackPawnMove()", () => {
putPiece(chessboard, positionB6, pieces.blackKing);
let diagonalCapture: Move = {from: positionA7, to: positionB6, isValid: true}
expect(isValid.blackPawnMove(chessboard, diagonalCapture)).toBeFalsy();
expect(isPossible.blackPawnMove(chessboard, diagonalCapture)).toBeFalsy();
});
it("Pawns can capture pieces of a different color", () => {
......@@ -79,7 +79,7 @@ describe("Test blackPawnMove()", () => {
putPiece(chessboard, positionB6, pieces.whiteQueen);
let diagonalCapture: Move = {from: positionA7, to: positionB6, isValid: true}
expect(isValid.blackPawnMove(chessboard, diagonalCapture)).toBeTruthy();
expect(isPossible.blackPawnMove(chessboard, diagonalCapture)).toBeTruthy();
});
});
......@@ -93,32 +93,32 @@ describe("Test whitePawnMove()", () => {
putPiece(chessboard, positionD5, pieces.blackPawn); // Black Pawn at D5
let simpleFowardMove: Move = {from: positionD4, to: positionD5, isValid: true}
expect(isValid.whitePawnMove(chessboard, simpleFowardMove)).toBeFalsy();
expect(isPossible.whitePawnMove(chessboard, simpleFowardMove)).toBeFalsy();
});
it("Pawns can move forward", () => {
putPiece(chessboard, positionD4, pieces.whitePawn); // White Pawn at D4
let simpleFowardMove: Move = {from: positionD4, to: positionD5, isValid: true}
expect(isValid.whitePawnMove(chessboard, simpleFowardMove)).toBeTruthy();
expect(isPossible.whitePawnMove(chessboard, simpleFowardMove)).toBeTruthy();
});
it("When in the initial position, pawns can move two squares forward", () => {
putPiece(chessboard, positionD2, pieces.whitePawn); // White Pawn at D2
let doubleFowardMove: Move = {from: positionD2, to: positionD4, isValid: true}
expect(isValid.whitePawnMove(chessboard, doubleFowardMove)).toBeTruthy();
expect(isPossible.whitePawnMove(chessboard, doubleFowardMove)).toBeTruthy();
});
it("When they have already moved, pawns cannot move two squares forward", () => {
putPiece(chessboard, positionD3, pieces.whitePawn); // White Pawn at D3
let doubleFowardMove: Move = {from: positionD3, to: positionD5, isValid: true}
expect(isValid.whitePawnMove(chessboard, doubleFowardMove)).toBeFalsy();
expect(isPossible.whitePawnMove(chessboard, doubleFowardMove)).toBeFalsy();
});
it("Pawns cannot capture an empty square", () => {
putPiece(chessboard, positionD3, pieces.whitePawn); // White Pawn at D3
let diagonalCapture: Move = {from: positionD3, to: positionC4, isValid: true}
expect(isValid.whitePawnMove(chessboard, diagonalCapture)).toBeFalsy();
expect(isPossible.whitePawnMove(chessboard, diagonalCapture)).toBeFalsy();
});
it("Pawns cannot capture a piece of the same color", () => {
......@@ -126,7 +126,7 @@ describe("Test whitePawnMove()", () => {
putPiece(chessboard, positionC4, pieces.whitePawn); // White Pawn at C4
let diagonalCapture: Move = {from: positionD3, to: positionC4, isValid: true}
expect(isValid.whitePawnMove(chessboard, diagonalCapture)).toBeFalsy();
expect(isPossible.whitePawnMove(chessboard, diagonalCapture)).toBeFalsy();
});
it("Pawns can capture pieces of a different color", () => {
......@@ -134,38 +134,133 @@ describe("Test whitePawnMove()", () => {
putPiece(chessboard, positionC4, pieces.blackKing); // Black King at C4
let diagonalCapture: Move = {from: positionD3, to: positionC4, isValid: true}
expect(isValid.whitePawnMove(chessboard, diagonalCapture)).toBeTruthy();
expect(isPossible.whitePawnMove(chessboard, diagonalCapture)).toBeTruthy();
})
});
/**
* TODO: Unit tests for function blackKingMove()
*/
describe("Test blackKingMove()", () => {
beforeEach( () => {
// TODO:
// Initialize an empty chessboard
});
it("A King can move 1 square in all directions", () => {
// Place a black King on E4 at the empty chessboard
// Check it can move to squares D3, D4, D5, E3, E5, F3, F4, and F5
})
it("A King cannot move more than 1 square", () => {
// Place a black King on E4 at the empty chessboard
// Check it cannot move to squares C2, C3, C4, C6, D4, and F4
})
it("A King cannot capure pieces from the same color", () => {
// Place a black King on E4 at the empty chessboard
// Place a black Pawn on E5
// Check the King cannot move to E5.
})
it("A King can capure pieces from a different color", () => {
// Place a black King on E4 at the empty chessboard
// Place a white Pawn on E5
// Check the King can move to E5.
})
});
/**
* TODO: Unit tests for function blackQueenMove()
*/
describe("Test blackQueenMove()", () => {
beforeEach( () => {
// TODO:
// Initialize an empty chessboard
});
it("A Queen can move several squares in any direction", () => {
// TODO:
});
it("A Queen can only move horizontally, vertically, and diagonally", () => {
// TODO:
});
it("A Queen cannot move when there are other pieces between the initial and final squares", () => {
// TODO:
});
it("A Queen cannot capure pieces from the same color", () => {
// TODO:
});
it("A Queen can capure pieces from a different color", () => {
// TODO:
});
});
/**
* TODO: Unit tests for function blackBishopMove()
*/
describe("Test blackBishopMove()", () => {
beforeEach( () => {
// TODO:
// Initialize an empty chessboard
});
it("A Bishop can move diagonally", () => {
// TODO:
});
it("A Bishop cannot move horizontally", () => {
// TODO:
});
it("A Bishop cannot move vertically", () => {
// TODO:
});
it("A Bishop can capture a piece from another color", () => {
// TODO:
});
it("A Bishop cannot capture a piece from the same color", () => {
// TODO:
});
it("A Bishop cannot move when there are other pieces between the initial and final squares", () => {
// TODO:
});
});
/**
* TODO: Unit tests for function blackKnightMove()
*/
describe("Test blackKnightMove()", () => {
beforeEach( () => {
// TODO:
// Initialize an empty chessboard
});
it("A Knight can move two squares horizontally and one square vertically", () => {
// TODO:
})
it("A Knight can move two squares vertically and one square horizontally", () => {
// TODO:
})
it("A Knight can 'jump' obstacles" , () => {
// TODO:
})
it("A Knight can move diagonally", () => {
// TODO:
});
it("A Knight cannot move horizontally", () => {
// TODO:
});
it("A Knight cannot move vertically", () => {
// TODO:
});
it("A Knight can capture a piece from another color", () => {
// TODO:
});
it("A Knight cannot capture a piece from the same color", () => {
// TODO:
});
});
/**
* TODO: Unit tests for function blackRoockMove()
*/
describe("Test blackRoockMove()", () => {
beforeEach( () => {
// TODO:
......@@ -192,10 +287,41 @@ describe("Test blackRoockMove()", () => {
// Check the roock cannot move to A8
// Check the roock cannot mobe to H7
});
it("A roock can capture a piece from another color", () => {
// TODO:
});
it("A roock cannot capture a piece from the same color", () => {
// TODO:
});
it("A Roock cannot move when there are other pieces between the initial and final squares", () => {
// TODO:
});
});
/**
* TODO: Unit tests for function whiteKingMove()
*/
describe("Test whiteKingMove()", () => {});
/**
* TODO: Unit tests for function whiteQueenMove()
*/
describe("Test whiteQueenMove()", () => {});
/**
* TODO: Unit tests for function whiteBishopMove()
*/
describe("Test whiteBishopMove()", () => {});
/**
* TODO: Unit tests for function whiteKnightMove()
*/
describe("Test whiteKnightMove()", () => {});
/**
* TODO: Unit tests for function whiteRoockMove()
*/
describe("Test whiteRoockMove()", () => {});
\ No newline at end of file
import { Chessboard, squareAtPosition, Square, pieceAtPosition, isEmpty } from './chessboard'
import { Position, top, bottom, right, left, equals } from "./position";
import { Chessboard, squareAtPosition, Square } from './chessboard'
import { Position } from "./position";
import * as pieces from './piece'
import {Piece} from './piece'
import * as isValid from './move-validation'
import * as isPossible from './move-validation'
const VALID_MOVE_STRING: RegExp = new RegExp('([a-z]|[A-Z])([1-8])-([A-H]|[a-z])([1-8])')
......@@ -12,9 +12,17 @@ export interface Move {
to? : Position;
}
export function processMove(chessboard:Chessboard, movementString: string): boolean {
let move : Move = parseMoveString(movementString);
//console.log("Move: " + move.from.column + move.from.line + move.to.column + move.to.line);
/**
* Processes a move received from a client browser.
* If the move is valid and possible, the move is performed and this function
* returns true. Otherwiser, it returns false
*
* @param chessboard The chessboard for the current game
* @param moveString The string received from the client containing a move
* @returns true, if the move is valid and possible
*/
export function processMove(chessboard:Chessboard, moveString: string): boolean {
let move : Move = parseMoveString(moveString);
if (move.isValid && isMovePossible(chessboard, move)) {
performMove(chessboard, move);
......@@ -25,6 +33,12 @@ export function processMove(chessboard:Chessboard, movementString: string): bool
return true;
}
/**
* Parses a string in the format "A1-F8" and returns a Move.
* If the format is not valid, returns a Move with isValid === true.
*
* @param movementString A 5 characters string containing a move
*/
export function parseMoveString(movementString: string): Move {
let newMove : Move;
if (movementString.length != 5 || ! movementString.match(VALID_MOVE_STRING)) {
......@@ -46,6 +60,11 @@ export function parseMoveString(movementString: string): Move {
return newMove;
}
/**
* Checks whether a move is possible in the given chessboard
* @param chessboard
* @param move
*/
function isMovePossible(chessboard : Chessboard, move : Move): boolean {
let square: Square = squareAtPosition(chessboard, move.from!);
if (square.isEmpty) { return false;}
......@@ -53,18 +72,18 @@ function isMovePossible(chessboard : Chessboard, move : Move): boolean {
let piece : Piece = square.piece!;
switch(piece) {
case pieces.whitePawn : return isValid.whitePawnMove(chessboard, move);
case pieces.whiteKing : return isValid.whiteKingMove(chessboard, move);
case pieces.whiteQueen : return isValid.whiteQueenMove(chessboard, move);
case pieces.whiteBishop: return isValid.whiteBishopMove(chessboard, move);
case pieces.whiteKnight: return isValid.whiteKnightMove(chessboard, move);
case pieces.whiteRoock : return isValid.whiteRoockMove(chessboard, move);
case pieces.blackPawn : return isValid.blackPawnMove(chessboard, move);
case pieces.blackKing : return isValid.blackKingMove(chessboard, move);
case pieces.blackQueen : return isValid.blackQueenMove(chessboard, move);
case pieces.blackBishop: return isValid.blackBishopMove(chessboard, move);
case pieces.blackKnight: return isValid.blackKnightMove(chessboard, move);
case pieces.blackRoock : return isValid.blackRoockMove(chessboard, move);
case pieces.whitePawn : return isPossible.whitePawnMove(chessboard, move);
case pieces.whiteKing : return isPossible.whiteKingMove(chessboard, move);
case pieces.whiteQueen : return isPossible.whiteQueenMove(chessboard, move);
case pieces.whiteBishop: return isPossible.whiteBishopMove(chessboard, move);
case pieces.whiteKnight: return isPossible.whiteKnightMove(chessboard, move);
case pieces.whiteRoock : return isPossible.whiteRoockMove(chessboard, move);
case pieces.blackPawn : return isPossible.blackPawnMove(chessboard, move);
case pieces.blackKing : return isPossible.blackKingMove(chessboard, move);
case pieces.blackQueen : return isPossible.blackQueenMove(chessboard, move);
case pieces.blackBishop: return isPossible.blackBishopMove(chessboard, move);
case pieces.blackKnight: return isPossible.blackKnightMove(chessboard, move);
case pieces.blackRoock : return isPossible.blackRoockMove(chessboard, move);
}
return false;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment