Advertisement
Guest User

Untitled

a guest
Apr 25th, 2019
83
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 10.17 KB | None | 0 0
  1. export class ConnectBoard {
  2. static DRAW = 3;
  3. static RED_PLAYER = 1;
  4. static BLUE_PLAYER = 2;
  5. static EMPTY_VALUE= 0;
  6.  
  7. constructor(rows, columns, solver){
  8. if (!solver){
  9. throw new Error("solver required to be provided");
  10. }
  11.  
  12. if (typeof solver.SolveTable !== "function"){
  13. throw new Error("solver.SolveTable function required from Solver");
  14. }
  15.  
  16. this.table = [];
  17. this.rows = rows;
  18. this.columns = columns;
  19.  
  20. this.winner = -1;
  21. this.redMoves = 0;
  22. this.blueMoves = 0;
  23. this.solver = solver;
  24. this.totalMoves = rows * columns;
  25. this.resetTable();
  26. }
  27.  
  28. isPlayable(){
  29. return this.winner === -1;
  30. }
  31.  
  32. // isDraw returns true/false if giving table was a draw.
  33. isDraw(){
  34. return (this.blueMoves + this.redMoves) === this.totalMoves;
  35. }
  36.  
  37. // movesMade returns giving total moves made by both players.
  38. movesMade(){
  39. return this.blueMoves + this.redMoves;
  40. }
  41.  
  42. // resetTable resets the underline this.table table.
  43. resetTable(){
  44. const table = [];
  45. for (let i = 0; i < this.rows; i++){
  46. const columns = [];
  47. for (let j = 0; j < this.columns; j++){
  48. columns.push(ConnectBoard.EMPTY_VALUE);
  49. }
  50. table.push(columns);
  51. }
  52.  
  53. this.winner = -1;
  54. this.redMoves = 0;
  55. this.blueMoves = 0;
  56. this.table = table;
  57. }
  58.  
  59. // Play runs a brute-force method where we check if a giving
  60. // set of moves by either player reaches a draw or win.
  61. //
  62. // Callback will be called with the following arguments:
  63. //
  64. // 1. An error object if any
  65. // 2. A integer indicating state: -1 for no-win, 0 for draw and 1 or 2
  66. // for a winner of game.
  67. //
  68. // Whatever solver is used is expected to follow the underline argument
  69. // format.
  70. Play(row, column, player, callback){
  71. if (player !== ConnectBoard.RED_PLAYER && player !== ConnectBoard.BLUE_PLAYER){
  72. callback(new Error("invalid player provided"));
  73. return null;
  74. }
  75.  
  76. if (row >= this.table.length || row < 0) {
  77. callback(new Error("invalid row"));
  78. return null;
  79. }
  80.  
  81. const targetRow = this.table[row];
  82. if (column >= targetRow.length || column < 0) {
  83. callback(new Error("invalid column"));
  84. return null;
  85. }
  86.  
  87. if (targetRow[column]) {
  88. callback(new Error("unplayable column"));
  89. return null;
  90. }
  91.  
  92. if (!this.isPlayable()){
  93. callback(new Error("game already completed, please reset"));
  94. return null;
  95. }
  96.  
  97. if (player === ConnectBoard.RED_PLAYER){
  98. this.redMoves++;
  99. }
  100.  
  101. if (player === ConnectBoard.BLUE_PLAYER){
  102. this.blueMoves++;
  103. }
  104.  
  105. // set giving column point.
  106. targetRow[column] = player;
  107.  
  108. // if neither players have made at least 4 moves, then
  109. // there isn't a need to check,just skip.
  110. if (this.redMoves < 4 && this.blueMoves < 4){
  111. callback(null, -1);
  112. return null;
  113. }
  114.  
  115. this.solver.SolveTable(this.table, function (err, state) {
  116. if (err){
  117. callback(err, state);
  118. return null;
  119. }
  120.  
  121. // if it's an ongoing game, then pass back to callback.
  122. if (state === -1){
  123. callback(null, state);
  124. return null;
  125. }
  126.  
  127. // set winner and pass to callback.
  128. this.winner = state;
  129. callback(null, state)
  130. }.bind(this));
  131. }
  132. }
  133.  
  134.  
  135. export class BruteForceSolver {
  136. constructor(rows, columns, emptyValue){
  137. this.columns = columns;
  138. this.rows = rows;
  139. this.emptyValue = emptyValue;
  140. }
  141.  
  142. // SolveTable solves the provided table matching
  143. // expected rows and columns range.
  144. //
  145. // Callback is provided with two arguments:
  146. //
  147. // 1. An error if any
  148. // 2. A integer value indicating a draw (0), win (1 or 2) or
  149. // on going game -1.
  150. //
  151. // A second argument providing the value in this case
  152. // 1 or 2 which won the game, or a -1 if it was a draw.
  153. // If the game is still ongoing then a 0 is giving as
  154. // second value, indicating no win or draw yet.
  155. SolveTable(table, callback) {
  156. if (!table){
  157. callback(new Error("invalid table argument"));
  158. return null;
  159. }
  160.  
  161. if (table.length < this.rows){
  162. callback(new Error("table with unmatched row length"));
  163. return null;
  164. }
  165.  
  166. const section = table[0];
  167. if (section.length < this.columns){
  168. callback(new Error("table with unmatched column length"));
  169. return null;
  170. }
  171.  
  172. const vertResult = this.checkVertical(table);
  173. if (vertResult){
  174. callback(null, vertResult);
  175. return null;
  176. }
  177.  
  178. const horizResult = this.checkHorizontal(table);
  179. if (horizResult){
  180. callback(null, horizResult);
  181. return null;
  182. }
  183.  
  184. const dLeft = this.checkDiagonalLeft(table);
  185. if (dLeft){
  186. callback(null, dLeft);
  187. return null;
  188. }
  189.  
  190. const dRight = this.checkDiagonalRight(table);
  191. if (dRight){
  192. callback(null, dRight);
  193. return null;
  194. }
  195.  
  196. if (this.checkDraw(table)) {
  197. callback(null, 0);
  198. return null;
  199. }
  200.  
  201. return callback(null, -1);
  202. }
  203.  
  204. checkDraw(table) {
  205. for (let r = 0; r < this.rows; r++) {
  206. for (let c = 0; c < this.columns; c++) {
  207. if (table[r][c] === this.emptyValue) {
  208. return false;
  209. }
  210. }
  211. }
  212. return true;
  213. }
  214.  
  215. checkVertical(table) {
  216. // Check only if row is 3 or greater
  217. for (let r = 3; r < this.rows; r++) {
  218. for (let c = 0; c < this.columns; c++) {
  219. if (table[r][c]) {
  220. if (table[r][c] === table[r - 1][c] &&
  221. table[r][c] === table[r - 2][c] &&
  222. table[r][c] === table[r - 3][c]) {
  223. return table[r][c];
  224. }
  225. }
  226. }
  227. }
  228. return null;
  229. }
  230.  
  231. checkDiagonalLeft(table) {
  232. // Check only if row is 3 or greater AND column is 3 or greater
  233. for (let r = 3; r < this.rows; r++) {
  234. for (let c = 3; c < this.columns; c++) {
  235. if (table[r][c]) {
  236. if (table[r][c] === table[r - 1][c - 1] &&
  237. table[r][c] === table[r - 2][c - 2] &&
  238. table[r][c] === table[r - 3][c - 3]) {
  239. return table[r][c];
  240. }
  241. }
  242. }
  243. }
  244. return null;
  245. }
  246.  
  247. checkHorizontal(table) {
  248. // Check only if column is 3 or less
  249. for (let r = 0; r < this.rows; r++) {
  250. for (let c = 0; c < 4; c++) {
  251. if (table[r][c]) {
  252. if (table[r][c] === table[r][c + 1] &&
  253. table[r][c] === table[r][c + 2] &&
  254. table[r][c] === table[r][c + 3]) {
  255. return table[r][c];
  256. }
  257. }
  258. }
  259. }
  260. }
  261.  
  262. checkDiagonalRight(table) {
  263. // Check only if row is 3 or greater AND column is 3 or less
  264. for (let r = 3; r < this.rows; r++) {
  265. for (let c = 0; c < 4; c++) {
  266. if (table[r][c]) {
  267. if (table[r][c] === table[r - 1][c + 1] &&
  268. table[r][c] === table[r - 2][c + 2] &&
  269. table[r][c] === table[r - 3][c + 3]) {
  270. return table[r][c];
  271. }
  272. }
  273. }
  274. }
  275. }
  276.  
  277. // doVerticalCheck checks if giving table matches a
  278. // possible vertical win.
  279. //
  280. // Vertical checks can only make sense from row 3 and
  281. // above.
  282. static doVerticalCheck(table, r, c) {
  283. if (r < 3){
  284. return null;
  285. }
  286. if (table[r][c]) {
  287. if (table[r][c] === table[r - 1][c] &&
  288. table[r][c] === table[r - 2][c] &&
  289. table[r][c] === table[r - 3][c]) {
  290. return table[r][c];
  291. }
  292. }
  293. return null;
  294. }
  295.  
  296. // doDiagonalLeftCheck checks if giving diagonal positions
  297. // from row 3 and column 3 and above can be matched, since
  298. // we can't do diagonal checks when in row one and two.
  299. static doDiagonalLeftCheck(table, r, c) {
  300. if (r < 3){
  301. return null;
  302. }
  303. if (c < 3){
  304. return null;
  305. }
  306.  
  307. if (table[r][c]) {
  308. if (table[r][c] === table[r - 1][c - 1] &&
  309. table[r][c] === table[r - 2][c - 2] &&
  310. table[r][c] === table[r - 3][c - 3]) {
  311. return table[r][c];
  312. }
  313. }
  314. return null;
  315. }
  316.  
  317. // doHorizontalCheck checks giving row 4 length column for
  318. // a winning match.
  319. static doHorizontalCheck(table, r, c) {
  320. if (c > 4){
  321. return null;
  322. }
  323.  
  324. if (table[r][c]) {
  325. if (table[r][c] === table[r][c + 1] &&
  326. table[r][c] === table[r][c + 2] &&
  327. table[r][c] === table[r][c + 3]) {
  328. return table[r][c];
  329. }
  330. }
  331. return null;
  332. }
  333.  
  334. // doDiagonalRightCheck checks giving diagonal right row and column areas for
  335. // a winning match.
  336. static doDiagonalRightCheck(table, r, c) {
  337. if (r < 3){
  338. return null;
  339. }
  340. if (c > 4){
  341. return null;
  342. }
  343.  
  344. if (table[r][c]) {
  345. if (table[r][c] === table[r - 1][c + 1] &&
  346. table[r][c] === table[r - 2][c + 2] &&
  347. table[r][c] === table[r - 3][c + 3]) {
  348. return table[r][c];
  349. }
  350. }
  351. return null;
  352. }
  353.  
  354.  
  355. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement