@slugbugblue/trax 0.3.1 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/engine.js +42 -35
  2. package/package.json +5 -4
  3. package/tests.js +0 -11
package/engine.js CHANGED
@@ -1,4 +1,4 @@
1
- /* Copyright 2021 Chad Transtrum
1
+ /* Copyright 2019-2022 Chad Transtrum
2
2
  *
3
3
  * Licensed under the Apache License, Version 2.0 (the "License");
4
4
  * you may not use this file except in compliance with the License.
@@ -87,16 +87,17 @@ const Trax = class {
87
87
  this.notation = ''
88
88
  this.tiles = {}
89
89
  this.path = []
90
- this.count = 0
91
90
  this.invalid = false
92
91
  // moves might be a notation string
93
- if (typeof moves === 'string') moves = moves.split(' ')
94
- moves.forEach(move => {
95
- if (typeof move === 'string') {
96
- move = { move: this.move, notation: move }
97
- }
98
- this.play_move(move.move, move.notation)
99
- })
92
+ if (moves) {
93
+ if (typeof moves === 'string') moves = moves.split(' ')
94
+ moves.forEach(move => {
95
+ if (typeof move === 'string') {
96
+ move = { move: this.move, notation: move }
97
+ }
98
+ this.play_move(move.move, move.notation)
99
+ })
100
+ }
100
101
  }
101
102
 
102
103
  save () { // in order to evaluate potential moves, we have to modify the state
@@ -111,7 +112,6 @@ const Trax = class {
111
112
  notation: this.notation,
112
113
  tiles: JSON.stringify(this.tiles),
113
114
  path: JSON.stringify(this.path),
114
- count: this.count,
115
115
  invalid: this.invalid,
116
116
  }
117
117
  }
@@ -127,10 +127,13 @@ const Trax = class {
127
127
  this.notation = saved.notation
128
128
  this.tiles = JSON.parse(saved.tiles)
129
129
  this.path = JSON.parse(saved.path)
130
- this.count = saved.count
131
130
  this.invalid = saved.invalid
132
131
  }
133
132
 
133
+ get count () { // get the number of tiles currently in play
134
+ return Object.keys(this.tiles).length
135
+ }
136
+
134
137
  get color () { // get the color of the current player (w or b)
135
138
  return color_of[this.turn]
136
139
  }
@@ -154,7 +157,6 @@ const Trax = class {
154
157
  this.invalid = true // invalid tile is being played
155
158
  } else {
156
159
  this.tiles[id] = tile
157
- this.count++
158
160
  this.left = Math.min(this.left, x)
159
161
  this.right = Math.max(this.right, x)
160
162
  this.top = Math.min(this.top, y)
@@ -316,7 +318,6 @@ const Trax = class {
316
318
  tiles.forEach(tile => {
317
319
  if (winner) return // skip the remaining tiles once we find a win
318
320
  let ends = this.find_ends(color, tile.x, tile.y)
319
- if (ends.length !== 2) return // this should never happen, but if it does, don't die
320
321
  if (ends[0].x === ends[1].x && ends[0].y === ends[1].y) {
321
322
  winner = color // loop win
322
323
  this.path = ends[0].path // both paths are essentially the same
@@ -391,7 +392,7 @@ const Trax = class {
391
392
  dropped.push(this.add_tile(type, x, y)) // play the tile
392
393
  dropped = dropped.concat(this.forced_moves(x, y)) // play all forced moves
393
394
  if (this.invalid || tentative) {
394
- valid = false
395
+ if (this.invalid) valid = false
395
396
  this.restore(saved) // abort! abort!
396
397
  } else {
397
398
  this.check_win(dropped)
@@ -402,36 +403,42 @@ const Trax = class {
402
403
  return { dropped, notation, valid }
403
404
  }
404
405
 
405
- rotate_move (move) { // symmetry helper, rotate a move around the board
406
+ move_rotations (move) { // symmetry helper, rotate a move around the board
406
407
  let match = move.match(notation_regex)
407
- if (match === null) return move
408
- let x = this.width + 1 - Number(match[2])
409
- let y = decode_col(match[1])
410
- let s = { '/': '\\', '\\': '/', '+': '+'}[match[3]]
411
- return encode_col(x) + String(y) + s
412
- }
413
-
414
- provisional_notation (provisional) { // test a provisional move against the current state
415
- let prov = provisional.split(';') // return false if the provisional move does not match
416
- if (prov.length !== 4) return 'delete-provisional' // <-- if the provisional move is bad
417
- let notation = false // otherwise, return the notation required to make the move
418
- let rows = prov[1].split(':')
408
+ if (match === null) return []
409
+ let x = decode_col(match[1])
410
+ let y = Number(match[2])
411
+ let s = match[3]
412
+ let slashes = s === '+' ? [s] : ['/', '\\']
413
+ let moves = new Set()
414
+ // this isn't super precise, but it limits the search space ... maybe #TODO?
415
+ for (let slash of slashes) {
416
+ moves.add(encode_col(x) + String(y) + slash)
417
+ moves.add(encode_col(y) + String(x) + slash)
418
+ moves.add(encode_col(this.height - x + 1) + String(this.width - y + 1) + slash)
419
+ moves.add(encode_col(this.width - y + 1) + String(this.height -x + 1) + slash)
420
+ }
421
+ return [...moves]
422
+ }
423
+
424
+ provisional_move (from, to, via) { // test a provisional move against the current state
425
+ let rows = from.slice(1).split(':')
419
426
  let cols = rows.reduce((acc, cur) => Math.max(acc, code_row_len(cur)), 0)
420
427
  rows = rows.length
421
428
  if (this.width > rows && this.height > cols || this.width > cols && this.height > rows) {
422
429
  return 'delete-provisional' // current board is bigger than the provisional move
423
430
  }
424
- if (prov[1] === this.normalized) {
425
- let move = prov[2]
431
+ if (from === this.normalized) {
426
432
  let saved = this.save() // save this position
427
- for (let i = 0 ; i < 4 && !notation; i++) { // try all four orientations because symmetry
428
- this.drop_tile(move)
429
- if (prov[3] === this.normalized) notation = move // this move matched!
430
- this.restore(saved) // put the board back how it was
431
- move = this.rotate_move(move) // rotate to the next orientation
433
+ for (let move of this.move_rotations(via)) { // try all symmetrical moves
434
+ let drop = this.drop_tile(move)
435
+ if (drop.valid) {
436
+ if (to === this.normalized) return move // this move matched!
437
+ this.restore(saved) // put the board back how it was
438
+ }
432
439
  }
433
440
  }
434
- return notation
441
+ return false
435
442
  }
436
443
 
437
444
  get icon () {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@slugbugblue/trax",
3
- "version": "0.3.1",
3
+ "version": "0.4.0",
4
4
  "description": "Trax game engine and friends",
5
5
  "keywords": [
6
6
  "trax",
@@ -14,13 +14,14 @@
14
14
  "exports": "./engine.js",
15
15
  "repository": "gitlab:slugbugblue/trax",
16
16
  "scripts": {
17
- "test": "tape tests.js",
17
+ "test": "c8 tap --no-coverage",
18
18
  "preversion": "npm test",
19
19
  "postversion": "git push && git push --tags"
20
20
  },
21
21
  "devDependencies": {
22
- "eslint": "8.x",
23
- "tape": "5.x"
22
+ "c8": "^7.11.0",
23
+ "eslint": "^8.7.0",
24
+ "tap": "^15.1.6"
24
25
  },
25
26
  "type": "module",
26
27
  "engines": {
package/tests.js DELETED
@@ -1,11 +0,0 @@
1
- import test from 'tape'
2
- import Trax from './engine.js'
3
-
4
- test('create trax game', t => {
5
- let trax = new Trax('trax', '@0/', 'tape')
6
- t.equal(trax.id, 'tape', 'id is assigned')
7
- t.equal(trax.count, 1, '1 tile is played')
8
- t.equal(trax.turn, 2, 'player 2 is next')
9
- t.equal(trax.color, 'b', 'player 2 is black')
10
- t.end()
11
- })