Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- export class ConnectBoard {
- static DRAW = 3;
- static RED_PLAYER = 1;
- static BLUE_PLAYER = 2;
- static EMPTY_VALUE= 0;
- constructor(rows, columns, solver){
- if (!solver){
- throw new Error("solver required to be provided");
- }
- if (typeof solver.SolveTable !== "function"){
- throw new Error("solver.SolveTable function required from Solver");
- }
- this.table = [];
- this.rows = rows;
- this.columns = columns;
- this.winner = -1;
- this.redMoves = 0;
- this.blueMoves = 0;
- this.solver = solver;
- this.totalMoves = rows * columns;
- this.resetTable();
- }
- isPlayable(){
- return this.winner === -1;
- }
- // isDraw returns true/false if giving table was a draw.
- isDraw(){
- return (this.blueMoves + this.redMoves) === this.totalMoves;
- }
- // movesMade returns giving total moves made by both players.
- movesMade(){
- return this.blueMoves + this.redMoves;
- }
- // resetTable resets the underline this.table table.
- resetTable(){
- const table = [];
- for (let i = 0; i < this.rows; i++){
- const columns = [];
- for (let j = 0; j < this.columns; j++){
- columns.push(ConnectBoard.EMPTY_VALUE);
- }
- table.push(columns);
- }
- this.winner = -1;
- this.redMoves = 0;
- this.blueMoves = 0;
- this.table = table;
- }
- // Play runs a brute-force method where we check if a giving
- // set of moves by either player reaches a draw or win.
- //
- // Callback will be called with the following arguments:
- //
- // 1. An error object if any
- // 2. A integer indicating state: -1 for no-win, 0 for draw and 1 or 2
- // for a winner of game.
- //
- // Whatever solver is used is expected to follow the underline argument
- // format.
- Play(row, column, player, callback){
- if (player !== ConnectBoard.RED_PLAYER && player !== ConnectBoard.BLUE_PLAYER){
- callback(new Error("invalid player provided"));
- return null;
- }
- if (row >= this.table.length || row < 0) {
- callback(new Error("invalid row"));
- return null;
- }
- const targetRow = this.table[row];
- if (column >= targetRow.length || column < 0) {
- callback(new Error("invalid column"));
- return null;
- }
- if (targetRow[column]) {
- callback(new Error("unplayable column"));
- return null;
- }
- if (!this.isPlayable()){
- callback(new Error("game already completed, please reset"));
- return null;
- }
- if (player === ConnectBoard.RED_PLAYER){
- this.redMoves++;
- }
- if (player === ConnectBoard.BLUE_PLAYER){
- this.blueMoves++;
- }
- // set giving column point.
- targetRow[column] = player;
- // if neither players have made at least 4 moves, then
- // there isn't a need to check,just skip.
- if (this.redMoves < 4 && this.blueMoves < 4){
- callback(null, -1);
- return null;
- }
- this.solver.SolveTable(this.table, function (err, state) {
- if (err){
- callback(err, state);
- return null;
- }
- // if it's an ongoing game, then pass back to callback.
- if (state === -1){
- callback(null, state);
- return null;
- }
- // set winner and pass to callback.
- this.winner = state;
- callback(null, state)
- }.bind(this));
- }
- }
- export class BruteForceSolver {
- constructor(rows, columns, emptyValue){
- this.columns = columns;
- this.rows = rows;
- this.emptyValue = emptyValue;
- }
- // SolveTable solves the provided table matching
- // expected rows and columns range.
- //
- // Callback is provided with two arguments:
- //
- // 1. An error if any
- // 2. A integer value indicating a draw (0), win (1 or 2) or
- // on going game -1.
- //
- // A second argument providing the value in this case
- // 1 or 2 which won the game, or a -1 if it was a draw.
- // If the game is still ongoing then a 0 is giving as
- // second value, indicating no win or draw yet.
- SolveTable(table, callback) {
- if (!table){
- callback(new Error("invalid table argument"));
- return null;
- }
- if (table.length < this.rows){
- callback(new Error("table with unmatched row length"));
- return null;
- }
- const section = table[0];
- if (section.length < this.columns){
- callback(new Error("table with unmatched column length"));
- return null;
- }
- const vertResult = this.checkVertical(table);
- if (vertResult){
- callback(null, vertResult);
- return null;
- }
- const horizResult = this.checkHorizontal(table);
- if (horizResult){
- callback(null, horizResult);
- return null;
- }
- const dLeft = this.checkDiagonalLeft(table);
- if (dLeft){
- callback(null, dLeft);
- return null;
- }
- const dRight = this.checkDiagonalRight(table);
- if (dRight){
- callback(null, dRight);
- return null;
- }
- if (this.checkDraw(table)) {
- callback(null, 0);
- return null;
- }
- return callback(null, -1);
- }
- checkDraw(table) {
- for (let r = 0; r < this.rows; r++) {
- for (let c = 0; c < this.columns; c++) {
- if (table[r][c] === this.emptyValue) {
- return false;
- }
- }
- }
- return true;
- }
- checkVertical(table) {
- // Check only if row is 3 or greater
- for (let r = 3; r < this.rows; r++) {
- for (let c = 0; c < this.columns; c++) {
- if (table[r][c]) {
- if (table[r][c] === table[r - 1][c] &&
- table[r][c] === table[r - 2][c] &&
- table[r][c] === table[r - 3][c]) {
- return table[r][c];
- }
- }
- }
- }
- return null;
- }
- checkDiagonalLeft(table) {
- // Check only if row is 3 or greater AND column is 3 or greater
- for (let r = 3; r < this.rows; r++) {
- for (let c = 3; c < this.columns; c++) {
- if (table[r][c]) {
- if (table[r][c] === table[r - 1][c - 1] &&
- table[r][c] === table[r - 2][c - 2] &&
- table[r][c] === table[r - 3][c - 3]) {
- return table[r][c];
- }
- }
- }
- }
- return null;
- }
- checkHorizontal(table) {
- // Check only if column is 3 or less
- for (let r = 0; r < this.rows; r++) {
- for (let c = 0; c < 4; c++) {
- if (table[r][c]) {
- if (table[r][c] === table[r][c + 1] &&
- table[r][c] === table[r][c + 2] &&
- table[r][c] === table[r][c + 3]) {
- return table[r][c];
- }
- }
- }
- }
- }
- checkDiagonalRight(table) {
- // Check only if row is 3 or greater AND column is 3 or less
- for (let r = 3; r < this.rows; r++) {
- for (let c = 0; c < 4; c++) {
- if (table[r][c]) {
- if (table[r][c] === table[r - 1][c + 1] &&
- table[r][c] === table[r - 2][c + 2] &&
- table[r][c] === table[r - 3][c + 3]) {
- return table[r][c];
- }
- }
- }
- }
- }
- // doVerticalCheck checks if giving table matches a
- // possible vertical win.
- //
- // Vertical checks can only make sense from row 3 and
- // above.
- static doVerticalCheck(table, r, c) {
- if (r < 3){
- return null;
- }
- if (table[r][c]) {
- if (table[r][c] === table[r - 1][c] &&
- table[r][c] === table[r - 2][c] &&
- table[r][c] === table[r - 3][c]) {
- return table[r][c];
- }
- }
- return null;
- }
- // doDiagonalLeftCheck checks if giving diagonal positions
- // from row 3 and column 3 and above can be matched, since
- // we can't do diagonal checks when in row one and two.
- static doDiagonalLeftCheck(table, r, c) {
- if (r < 3){
- return null;
- }
- if (c < 3){
- return null;
- }
- if (table[r][c]) {
- if (table[r][c] === table[r - 1][c - 1] &&
- table[r][c] === table[r - 2][c - 2] &&
- table[r][c] === table[r - 3][c - 3]) {
- return table[r][c];
- }
- }
- return null;
- }
- // doHorizontalCheck checks giving row 4 length column for
- // a winning match.
- static doHorizontalCheck(table, r, c) {
- if (c > 4){
- return null;
- }
- if (table[r][c]) {
- if (table[r][c] === table[r][c + 1] &&
- table[r][c] === table[r][c + 2] &&
- table[r][c] === table[r][c + 3]) {
- return table[r][c];
- }
- }
- return null;
- }
- // doDiagonalRightCheck checks giving diagonal right row and column areas for
- // a winning match.
- static doDiagonalRightCheck(table, r, c) {
- if (r < 3){
- return null;
- }
- if (c > 4){
- return null;
- }
- if (table[r][c]) {
- if (table[r][c] === table[r - 1][c + 1] &&
- table[r][c] === table[r - 2][c + 2] &&
- table[r][c] === table[r - 3][c + 3]) {
- return table[r][c];
- }
- }
- return null;
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement