123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328 |
- // 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(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 (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
- 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]
- ];
- module.exports = GameMachine;
|