@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.
- package/engine.js +42 -35
- package/package.json +5 -4
- package/tests.js +0 -11
package/engine.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* Copyright
|
|
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 (
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
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
|
-
|
|
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
|
|
408
|
-
let x =
|
|
409
|
-
let y =
|
|
410
|
-
let s =
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
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 (
|
|
425
|
-
let move = prov[2]
|
|
431
|
+
if (from === this.normalized) {
|
|
426
432
|
let saved = this.save() // save this position
|
|
427
|
-
for (let
|
|
428
|
-
this.drop_tile(move)
|
|
429
|
-
if (
|
|
430
|
-
|
|
431
|
-
|
|
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
|
|
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
|
+
"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": "
|
|
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
|
-
"
|
|
23
|
-
"
|
|
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
|
-
})
|