3 Коміти 019ddd0da3 ... e6e883a2fb

Автор SHA1 Опис Дата
  Dim Dim e6e883a2fb Match moves run 6 роки тому
  Dim Dim 174c4bc951 Merge branch 'master' of http://git.onarbooks.com/LosPoulos/mafalda-api 6 роки тому
  Dim Dim 948bb15631 before pull 6 роки тому
4 змінених файлів з 392 додано та 248 видалено
  1. 5 0
      game/index.js
  2. 318 213
      game/machine/GameMachine.js
  3. 59 29
      game/machine/GameMoves.js
  4. 10 6
      services/matchmaking/randomFree.js

+ 5 - 0
game/index.js

@@ -0,0 +1,5 @@
+const GameMachine = require('./machine/GameMachine');
+
+module.exports = {
+    GameMachine
+};

+ 318 - 213
game/machine/GameMachine.js

@@ -1,223 +1,328 @@
-var Card = require('./Card.js');
+// var Card = require('./Card.js');
+
+const SHA256 = require('crypto-js/sha256');
+
+// TODO test
+
+// Need to create an initialization move or something to get verified!
+// Players can have their names placed on state.players on init, showing turns and colors
 
 class GameMachine {
-	constructor() {
-		this.state = {
-			board: new Board(),
-			players: [],
-			hash: "0123456789"
-		};
-
-	}
-
-	setState(state) {
-		this.state = { ...this.state, ...state};
-	}
-
-	setBoard(board) {
-		this.state = { ...this.state, board};
-	}
-
-	setPlayers(players) {
-		this.state = { ...this.state, players};
-	}
-
-	runMove(move) {
-		move.performMove(this.state.board);
-	}
+    constructor(setup) {
+        this.state = {
+            board: new Board(),
+            players: [],
+            hash: '0123456789',
+            setup: setup,
+            stacks: {
+                moves: [],
+                hashes: []
+            }
+        };
+        this.setPlayers(setup.id);
+    }
+
+    getStack() {
+        return {
+            setup: this.state.setup,
+            stack: {
+                moves: this.state.stacks.moves.map((move) => move.export()),
+                hashes: this.state.stacks.hashes
+            }
+        };
+    }
+
+    flush() {
+        console.log(this.state);
+    }
+
+    setState(state) {
+        this.state = { ...this.state, ...state};
+    }
+
+    setBoard(board) {
+        this.state = { ...this.state, board};
+    }
+
+    save() {
+        return {...this.state, board: this.state.board.save()};
+    }
+
+    load(state) {
+        this.state.board.load(state.board);
+        this.state = { ...state, board: this.state.board };
+    }
+
+    setPlayers(players) {
+        this.state = { ...this.state, players};
+    }
+
+    getMyTeam(token) {
+        return this.state.setup.id.indexOf(token);
+    }
+
+    getPlayerNumber(player) {
+        return this.state.players[0] === player ? 0 : 1;
+        // return this.state.players[0] === UserService.getUsername() ? 0 : 1;
+    }
+
+    getPositionTeam(x) {
+        return this.state.players.indexOf(this.state.board.owners[x]);
+    }
+
+    hasFinished() {
+        return this.state.stacks.hashes.length >= 10;
+    }
+
+    getWinner() {
+        const score1 = this.state.board.getScore(this.state.players[0]);
+        const score2 = this.state.board.getScore(this.state.players[1]) + 1;
+        return  score1 === score2 ? -1
+            : (
+                score1 > score2
+                    ? this.state.players[0]
+                    : this.state.players[1]
+            );
+    }
+
+    isMyTurn(player) {
+        let moves = this.state.stacks.hashes.length;
+        return this.getPlayerNumber(player) === moves % 2 && moves < 10;
+    }
+
+    needFinalization() {
+        return this.state.stacks.hashes.length === 9;
+    }
+
+    runMove(move) {
+        const spray = SHA256(JSON.stringify(move)).toString();
+        if (this.state.stacks.hashes.includes(spray)) {
+            console.log('This move has been processed already');
+            return;
+        }
+        try {
+            move.verify(this.state);
+            move.performMove(this.state.board);
+            this.state.stacks.moves.push(move);
+            this.state.stacks.hashes.push(spray);
+            console.log(this.state.stacks);
+        } catch (e) {
+            throw e;
+        }
+    }
+
+    ownerOf(x) {
+        return this.state.board.owners[x];
+    }
+
+    runMatch(stack) {
+        for (let i = 0; i < 9; i++) {
+            this.runMove(stack[i]);
+        }
+        return this.getWinner();
+    }
 }
 
 class Board {
-	constructor() {
-		this.data = [];
-		this.owners = [];
-
-		// These 2 to be updated after every move
-		this.triggerPaths = []; // like attackVectors
-		this.plusPaths = []; // like attackVectors
-		for (var i=0; i < 9; i += 1) {
-			this.triggerPaths[i] = [];
-			this.plusPaths[i] = {
-				winner: -1,
-				sums: []
-			}
-		}
-	}
-
-	debug() {
-		console.log('BOARD');
-		console.log('---------------------------');
-		console.log('Cards :');
-		let s = '';
-		for(var i=0,j=0;i<this.data.length;i+=1) {
-			s += this.owners[i] + ':' + this.data[i] + '\t';
-			j += 1;
-			if(j%3===0)s += '\n';
-		}
-		console.log(s);
-		console.log('---------------------------');
-		console.log('Triggers');
-		console.log('---------------------------');
-		s = '';
-		for(var i=0,j=0;i<this.triggerPaths.length;i+=1) {
-			s += '[' + this.triggerPaths[i] + ']' + '\t\t';
-			j += 1;
-			if(j%3===0)s += '\n';
-		}
-		console.log(s);
-		console.log('---------------------------');
-		console.log('Pluspaths');
-		console.log('---------------------------');
-		s = '';
-		for(var i=0,j=0;i<this.plusPaths.length;i+=1) {
-			s += '[' + (this.plusPaths[i].winner) + ']' + '\t\t';
-			j += 1;
-			if(j%3===0)s += '\n';
-		}
-		console.log(s);
-		console.log('---------------------------');
-
-	}
-
-	putCard(card, position, player) { // position = 0-9
-		this.data[ position ] = card;
-		this.owners[position] = player;
-
-		this._calculatePlusAndTriggers(position);
-		this._analyze(position);
-	}
-
-	_calculatePlusAndTriggers(position) {
-		const attacker 		= this.data[position];
-		for (let j = 0; j < 4; j += 1) {
-			if ( Board.ATTACK_VECTORS[position][j] === 0 ) continue;
-			
-			const dx = this._getDisplacement(j);
-			const attackedCard  = this.data[position + dx];
-			const defendJ = ( j + 2 ) % 4;
-
-			if (attackedCard) {
-				const sum = attacker.attack[j] + attackedCard.attack[defendJ];
-				this.plusPaths[	 	 position  ].sums[j] = sum;
-				this.plusPaths[   position + dx].sums[defendJ] = sum;
-				this.triggerPaths[	 position  ][j] =   attacker.attack[j] - attackedCard.attack[defendJ];
-				this.triggerPaths[position + dx][defendJ] =   -this	.triggerPaths[position][j]; // opposite to the above
-			}
-		}
-	}
-
-	_getDisplacement(j) {
-		switch(j) {
-			case 0: return 1;//return {x: 1, y:0};
-			case 1: return -3;//return {x: 0, y:1};
-			case 2: return -1;//return {x: -1, y:0};
-			case 3: return 3;//return {x: 0, y:-1};
-			default: throw Error("Cannot _getDisplacement of this value: " + j);
-		}
-	}
-
-	_flipCard(position, combo, owner) {
-		// change owner!
-
-		this.owners[position] = owner;
-		if(combo) this._analyze(position, combo);
-	}
-
-	_analyze(position, combo) {
-		// check rules!
-		if(			this._checkSameRule(position) && !combo ) {
-			// Apply Same Rule
-			this._applySameRule(position);
-		} else if ( this._checkPlusRule(position) && !combo ) {
-			// Apply Plus Rule
-			this._applyPlusRule(position);
-		} else {
-			this._applyAttackRule(position);
-		}
-	}
-
-	_applyAttackRule(position) { //seems ok
-		if ( !this.triggerPaths[position] ) return;
-		for (let j = 0; j < 4; j += 1) {
-			if ( Board.ATTACK_VECTORS[position][j] === 0 ) continue;
-			const dx = this._getDisplacement(j);
-			if ( this.triggerPaths[position][j] > 0 ) {
-				this._flipCard(position + dx, false, this.owners[position]);
-			}
-		}
-	}
-
-	_applySameRule(position) {
-		for (let j = 0; j < 4; j += 1) {
-			if ( Board.ATTACK_VECTORS[position][j] === 0 ) continue;
-			const dx = this._getDisplacement(j);
-			if ( this.triggerPaths[position][j] === 0 ) {
-				this._flipCard(position + dx, true, this.owners[position]);
-			}
-		}	
-	}
-
-	_applyPlusRule(position) {
-		for (let j = 0; j < 4; j += 1) { // this for loop can be fixed seems obsolete & slow
-			if ( Board.ATTACK_VECTORS[position][j] === 0 ) continue;
-			const dx = this._getDisplacement(j);
-			if ( this.plusPaths[position].sums[j] === this.plusPaths[position].winner ) {
-				this._flipCard(position + dx, true, this.owners[position]);
-			}
-		}
-	}
-
-	_checkSameRule(position) {
-		const card = this.data[position];
-		let  sames = 0;
-		for (let j = 0; j < 4; j += 1) {
-			if ( Board.ATTACK_VECTORS[position][j] === 0 ) continue;			
-			if (this.triggerPaths[position] && 
-				this.triggerPaths[position][j] === 0) {
-				sames += 1;
-			}
-		}	
-		return sames > 1;
-	}
-
-	_checkPlusRule(position) { // refactor plz // need fix (problem double plus a.k.a. four side attack)
-		const card = this.data[position];
-		let pluses = {};
-		let sum = -1;
-		for (let j = 0; j < 4; j += 1) {
-			if ( Board.ATTACK_VECTORS[position][j] === 0 ) continue;
-			if(sum = this.plusPaths[position].sums[j])
-				pluses[sum] = (pluses[sum] || 0) + 1;
-		}
-		for (var i in pluses) {
-			if (pluses[i] > 1) {
-				this.plusPaths[position].winner = parseInt(i);
-				console.log(i);
-				return true;
-			}
-		}
-		this.plusPaths[position].winner = -1;
-		return false;
-	}
-
-	isEmpty(position) {
-		return !!this.data[position];
-	}
+    constructor() {
+        this.data = [];
+        this.owners = [];
+
+        // These 2 to be updated after every move
+        this.triggerPaths = []; // like attackVectors
+        this.plusPaths = []; // like attackVectors
+        for (let i=0; i < 9; i += 1) {
+            this.triggerPaths[i] = [];
+            this.plusPaths[i] = {
+                winners: [],
+                sums: []
+            };
+        }
+    }
+
+    getScore(player) {
+        return this.owners.reduce((a,b) => b === player ? a + 1 : a, 0);
+    }
+
+    debug() {
+        console.log('BOARD');
+        console.log('---------------------------');
+        console.log('Cards :');
+        let s = '';
+        for(let i=0,j=0;i<9;i+=1) {
+            s += `P${this.owners[i]}:${(this.data[i] ? this.data[i].attack.reduce((a,b) => a + b + '|', '|') : 'Empty')}\t`;
+            j += 1;
+            if(j%3===0)s += '\n';
+        }
+        console.log(s);
+        console.log('---------------------------');
+        console.log('Triggers');
+        console.log('---------------------------');
+        s = '';
+        for(let i=0,j=0;i<9;i+=1) {
+            s += `[${this.triggerPaths[i]}]\t\t`;
+            j += 1;
+            if(j%3===0)s += '\n';
+        }
+        console.log(s);
+        console.log('---------------------------');
+        console.log('Pluspaths');
+        console.log('---------------------------');
+        s = '';
+        for(let i=0,j=0;i<9;i+=1) {
+            s += `[${(this.plusPaths[i].winners.reduce((a,b) => a + b + '|', '|'))}]\t\t`;
+            j += 1;
+            if(j%3===0)s += '\n';
+        }
+        console.log(s);
+        console.log('---------------------------');
+
+    }
+
+    putCard(card, position, player) { // position = 0-9
+        this.data[ position ] = card;
+        this.owners[position] = player;
+
+        this._calculatePlusAndTriggers(position);
+        this._analyze(position);
+    }
+
+    _calculatePlusAndTriggers(position) {
+        const attacker = this.data[position];
+        for (let j = 0; j < 4; j += 1) {
+            if ( Board.ATTACK_VECTORS[position][j] === 0 ) continue;
+
+            const dx = this._getDisplacement(j);
+            const attackedCard  = this.data[position + dx];
+            const defendJ = ( j + 2 ) % 4;
+
+            if (attackedCard) {
+                const sum = attacker.attack[j] + attackedCard.attack[defendJ];
+                this.plusPaths[      position  ].sums[j] = sum;
+                this.plusPaths[   position + dx].sums[defendJ] = sum;
+                this.triggerPaths[   position  ][j] =   attacker.attack[j] - attackedCard.attack[defendJ];
+                this.triggerPaths[position + dx][defendJ] =   -this.triggerPaths[position][j]; // opposite to the above
+            }
+        }
+    }
+
+    _getDisplacement(j) {
+        switch(j) {
+            case 0: return 1;   //return {x: 1, y:0};
+            case 1: return -3;  //return {x: 0, y:1};
+            case 2: return -1;  //return {x: -1, y:0};
+            case 3: return 3;   //return {x: 0, y:-1};
+            default: throw Error ('Cannot _getDisplacement of this value: ' + j);
+        }
+    }
+
+    _flipCard(position, combo, owner) {
+        // change owner!
+        let isFlipping = owner !== this.owners[position];
+        this.owners[position] = owner;
+        if(combo && isFlipping) this._analyze(position, combo);
+    }
+
+    _analyze(position, combo) {
+        // check rules!
+        if(         this._checkSameRule(position) && !combo ) {
+            // Apply Same Rule
+            this._applySameRule(position);
+        }
+        if ( this._checkPlusRule(position) && !combo ) {
+            // Apply Plus Rule
+            this._applyPlusRule(position);
+        }
+        this._applyAttackRule(position, combo);
+    }
+
+    _applyAttackRule(position, combo = false) { //seems ok
+        if ( !this.triggerPaths[position] ) return;
+        for (let j = 0; j < 4; j += 1) {
+            if ( Board.ATTACK_VECTORS[position][j] === 0 ) continue;
+            const dx = this._getDisplacement(j);
+            if ( this.triggerPaths[position][j] > 0 ) {
+                this._flipCard(position + dx, combo, this.owners[position]);
+            }
+        }
+    }
+
+    _applySameRule(position) {
+        for (let j = 0; j < 4; j += 1) {
+            if ( Board.ATTACK_VECTORS[position][j] === 0 ) continue;
+            const dx = this._getDisplacement(j);
+            if ( this.triggerPaths[position][j] === 0 ) {
+                this._flipCard(position + dx, true, this.owners[position]);
+            }
+        }
+    }
+
+    _applyPlusRule(position) {
+        for (let j = 0; j < 4; j += 1) { // this for loop can be fixed seems obsolete & slow
+            if ( Board.ATTACK_VECTORS[position][j] === 0 ) continue;
+            const dx = this._getDisplacement(j);
+            if ( this.plusPaths[position].winners.includes(this.plusPaths[position].sums[j]) ) {
+                this._flipCard(position + dx, true, this.owners[position]);
+            }
+        }
+    }
+
+    _checkSameRule(position) {
+        let  sames = 0;
+        for (let j = 0; j < 4; j += 1) {
+            if ( Board.ATTACK_VECTORS[position][j] === 0 ) continue;
+            if (this.triggerPaths[position] &&
+                this.triggerPaths[position][j] === 0) {
+                sames += 1;
+            }
+        }
+        return sames > 1;
+    }
+
+    _checkPlusRule(position) { // refactor plz // need fix (problem double plus a.k.a. four side attack)
+        let pluses = {};
+        for (let j = 0; j < 4; j += 1) {
+            if ( Board.ATTACK_VECTORS[position][j] === 0 ) continue;
+            let sum = this.plusPaths[position].sums[j];
+            if ( sum ) {
+                pluses[sum] = (pluses[sum] || 0) + 1;
+            }
+        }
+        for (let i in pluses) {
+            if (pluses[i] > 1) {
+                this.plusPaths[position].winners.push(parseInt(i, 10));
+            }
+        }
+        return this.plusPaths[position].winners.length > 0;
+    }
+
+    isEmpty(position) {
+        return !this.data[position];
+    }
+
+    save() {
+        return {
+            data: this.data,
+            owners: this.owners
+        };
+    }
+    load(state) {
+        this.data = state.data;
+        this.owners = state.owners;
+    }
 }
-// START From top left goin row row  
+// START From top left goin row row
 Board.ATTACK_VECTORS = [
 //  [R, U, L, D] // right up left down
-	[1, 0, 0, 1],
-	[1, 0, 1, 1],
-	[0, 0, 1, 1],
-	[1, 1, 0, 1],
-	[1, 1, 1, 1],
-	[0, 1, 1, 1],
-	[1, 1, 0, 0],
-	[1, 1, 1, 0],
-	[0, 1, 1, 0]
+    [1, 0, 0, 1],
+    [1, 0, 1, 1],
+    [0, 0, 1, 1],
+    [1, 1, 0, 1],
+    [1, 1, 1, 1],
+    [0, 1, 1, 1],
+    [1, 1, 0, 0],
+    [1, 1, 1, 0],
+    [0, 1, 1, 0]
 ];
 
-module.exports = {
-	GameMachine
-};
+module.exports = GameMachine;

+ 59 - 29
game/machine/GameMoves.js

@@ -1,38 +1,68 @@
-class GameMove {	
-	constructor(type, data) {
-		this.type = type;
-		this.data = data;
-	}
-	verify() {}
-	performMove() {}
-};
+class GameMove {
+    constructor(type, data) {
+        this.type = type;
+        this.data = data;
+    }
+    verify() {}
+    performMove() {}
+}
 GameMove.TYPES = {
-	PLACE: 1,
-	PLACE_AND_REVEAL: 2,
-	SELECT_CARDS: 0
+    PLACE: 1,
+    REVEAL: 2,
+    SELECT_CARDS: 0
 };
 
 
 class PlaceMove extends GameMove {
-	constructor(card, position, player) {
-		super(GameMove.TYPES.PLACE, {});
-		this.position = position;
-		this.card = card;
-		this.player = player;
-	}
-	verify(state) {
-		if (state.board.isEmpty(this.position)) {
-			throw Error('Not a valid move, there is already a card there!');
-		}
-
-		return true;
-	}
-
-	performMove(board) {
-		board.putCard(this.card, this.position, this.player);
-	}
+    constructor(card, position, player) {
+        super(GameMove.TYPES.PLACE, {});
+        this.position = position;
+        this.card = card;
+        this.player = player;
+    }
+    verify(state) {
+        if (!state.board.isEmpty(this.position)) {
+            throw Error('Not a valid move, there is already a card there!');
+        }
+        // Verify the hash of the card to be sure it was there from the beginning
+        return true;
+    }
+
+    performMove(board) {
+        if(!board.isEmpty(this.position)) {
+            console.log(this.position);
+            console.log('Board');
+            console.log(board);
+
+
+            let err = { msg: 'Tried to place a card on occupied holder!' };
+            throw err;
+        }
+        board.putCard(this.card, this.position, this.player);
+    }
+}
+
+class RevealMove extends GameMove {
+    constructor(card, player) {
+        super(GameMove.TYPES.REVEAL, {});
+        this.card = card;
+        this.player = player;
+    }
+
+    verify(state) {
+        if (state.stack.length != 9) {
+            throw Error('Not a valid move, Need to play all cards to reveal!');
+        }
+        // TODO - Verify the last card from hashes
+        return true;
+    }
+
+    performMove() {
+        console.log('Reveal move processed');
+    }
 }
 
 module.exports = {
-	PlaceMove
+    PlaceMove,
+    RevealMove
 };

+ 10 - 6
services/matchmaking/randomFree.js

@@ -4,11 +4,12 @@ const nsp = io.of('/randomFree');
 const crypto = require('crypto-js');
 const cryptoSHA256= crypto.SHA256;
 
+const GameMachine= require('../../game').GameMachine;
+
 let Users = require('../../database').Repositories.Users;
 
-const gameService= {}; //require
-class randomFree {
 
+class randomFree {
 	constructor() {
 		nsp.on('connection', (socket) => {
 		  console.log('Someone connected');
@@ -62,10 +63,13 @@ class randomFree {
                     p1.on('broadcast', broadcast);
                     p2.on('broadcast', broadcast);
 
-                    const gameOver = (data) => {
-                        let result;
-                        // result = gameService.lastMove(data);
-                        nsp.in(channel).emit('result', result);
+                    const gameOver = (stack) => {
+                        // TODO this should be emitted ONLY if all 9 moves have been played
+                        //TODO verify move stack (compare with hashed cards)
+                        let gameMachine = new GameMachine(stack.setup);
+                        let winner = gameMachine.runMatch(stack.moves);
+
+                        nsp.in(channel).emit('winner', winner);
                     };
                     p1.once('gameOver', gameOver);
                     p2.once('gameOver', gameOver);