@slugbugblue/trax 0.13.0 → 0.15.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/CHANGELOG.md +29 -0
- package/README.md +10 -6
- package/package.json +1 -1
- package/src/analyst.js +195 -77
- package/src/cli.js +55 -2
- package/src/cmds/analyze.js +2 -2
- package/src/cmds/import-export.js +32 -2
- package/src/cmds/list.js +4 -4
- package/src/cmds/new.js +4 -1
- package/src/cmds/play-try.js +54 -7
- package/src/cmds/puzzles.js +158 -0
- package/src/cmds/suggest.js +1 -1
- package/src/cmds/view.js +1 -1
- package/src/puzzles.js +53 -39
- package/src/threats.js +78 -40
- package/src/tty.js +1 -1
- package/src/types.d.ts +56 -0
- package/src/version.js +1 -1
package/src/cmds/list.js
CHANGED
|
@@ -40,9 +40,6 @@ export const listCmd = {
|
|
|
40
40
|
],
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
const plural = (n, noun, nouns) =>
|
|
44
|
-
n + ' ' + (Math.abs(n) === 1 ? noun : nouns || noun + 's')
|
|
45
|
-
|
|
46
43
|
const alphaSort = (a, b) => {
|
|
47
44
|
a = String(a).toLowerCase()
|
|
48
45
|
b = String(b).toLowerCase()
|
|
@@ -104,6 +101,9 @@ listCmd.fn = (CLI, ...filters) => {
|
|
|
104
101
|
return CLI.error('No games. Type "new" to start a new game.')
|
|
105
102
|
}
|
|
106
103
|
|
|
104
|
+
const puzzle = filters.find((f) => f.length > 1 && 'puzzles'.startsWith(f))
|
|
105
|
+
if (puzzle) return CLI.do('puzzles', ...filters.filter((f) => f !== puzzle))
|
|
106
|
+
|
|
107
107
|
let list = []
|
|
108
108
|
const size = {
|
|
109
109
|
id: 1,
|
|
@@ -120,7 +120,7 @@ listCmd.fn = (CLI, ...filters) => {
|
|
|
120
120
|
id: (game.id === CLI.GAME.id ? '* #' : '#') + String(game.id),
|
|
121
121
|
name: game.name?.length || 0,
|
|
122
122
|
rules: game.rules,
|
|
123
|
-
moves: plural(moves, 'move'),
|
|
123
|
+
moves: CLI.plural(moves, 'move'),
|
|
124
124
|
smoves: game.over ? '9999' : String(moves).padStart(9, '0'),
|
|
125
125
|
sturn: String(game.turn),
|
|
126
126
|
p1: game.players?.[0] || 'white',
|
package/src/cmds/new.js
CHANGED
|
@@ -42,7 +42,7 @@ const pairs = [
|
|
|
42
42
|
['Donald', 'Daisy'],
|
|
43
43
|
['Fred', 'Barney'],
|
|
44
44
|
['Fred', 'George'],
|
|
45
|
-
['Frodo', '
|
|
45
|
+
['Frodo', 'Samwise'],
|
|
46
46
|
['Han', 'Chewie'],
|
|
47
47
|
['Harry', 'Ron'],
|
|
48
48
|
['Holmes', 'Watson'],
|
|
@@ -98,6 +98,9 @@ newCmd.fn = (CLI, variant, ...names) => {
|
|
|
98
98
|
let rules = ''
|
|
99
99
|
if (variant) {
|
|
100
100
|
variant = variant.toLowerCase()
|
|
101
|
+
if (variant.length > 1 && 'puzzles'.startsWith(variant)) {
|
|
102
|
+
return CLI.do('puzzles', 'new', ...names)
|
|
103
|
+
}
|
|
101
104
|
|
|
102
105
|
if (['8x8', '8', 'trax8x8', '8trax', '8x8trax'].includes(variant)) {
|
|
103
106
|
rules = 'trax8'
|
package/src/cmds/play-try.js
CHANGED
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
|
|
16
16
|
// CLI play/try commands
|
|
17
17
|
|
|
18
|
+
import { suggest } from '@slugbugblue/trax/analyst'
|
|
19
|
+
|
|
18
20
|
const moveRx = /^\d+\.?$/
|
|
19
21
|
const notationRx = /^[@a-z]+\d+[bps/\\+]$/i
|
|
20
22
|
|
|
@@ -46,6 +48,40 @@ export const tryCmd = {
|
|
|
46
48
|
],
|
|
47
49
|
}
|
|
48
50
|
|
|
51
|
+
const bot = (CLI) => {
|
|
52
|
+
const game = CLI.GAME
|
|
53
|
+
const trax = CLI.TRAX
|
|
54
|
+
const players = game.players || ['a', 'b']
|
|
55
|
+
const name = players[trax.turn - 1]
|
|
56
|
+
if (trax.over && game.puzzle) {
|
|
57
|
+
if (trax.move <= game.max && name !== 'puzzlebot') {
|
|
58
|
+
CLI.puzzleSolved(game.puzzle)
|
|
59
|
+
return 'win'
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return 'lose'
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (!trax.over && /bot\b/i.test(name)) {
|
|
66
|
+
if (game.puzzle && trax.move >= game.max) {
|
|
67
|
+
trax.play('puzzled')
|
|
68
|
+
return 'lose'
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
CLI.do('view')
|
|
72
|
+
CLI.out(CLI.bubble(trax.color + 'd', `${name} thinking...`))
|
|
73
|
+
const move = suggest(trax)?.pick?.move
|
|
74
|
+
if (move) {
|
|
75
|
+
CLI.out(CLI.bubble(trax.color + 'd', `${name} chooses ${move}`))
|
|
76
|
+
trax.play(move)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (game.puzzle) return ' and win by move ' + String(game.max)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return ''
|
|
83
|
+
}
|
|
84
|
+
|
|
49
85
|
playCmd.fn = (CLI, ...moves) => {
|
|
50
86
|
if (moves.length === 0 || !moves[0]) {
|
|
51
87
|
CLI.error('You must provide a move.')
|
|
@@ -56,24 +92,27 @@ playCmd.fn = (CLI, ...moves) => {
|
|
|
56
92
|
CLI.do('new')
|
|
57
93
|
}
|
|
58
94
|
|
|
59
|
-
|
|
95
|
+
const game = CLI.GAME
|
|
96
|
+
const trax = CLI.TRAX
|
|
97
|
+
|
|
98
|
+
if (trax.over) {
|
|
60
99
|
return CLI.error('The game is over.')
|
|
61
100
|
}
|
|
62
101
|
|
|
63
|
-
const start =
|
|
102
|
+
const start = trax.move
|
|
64
103
|
let checkMove
|
|
65
104
|
|
|
66
105
|
for (let move of moves) {
|
|
67
106
|
if (notationRx.test(move)) {
|
|
68
|
-
const moveNumber =
|
|
107
|
+
const moveNumber = trax.move
|
|
69
108
|
move = CLI.fixNotation(move)
|
|
70
109
|
if (checkMove) {
|
|
71
|
-
|
|
110
|
+
trax.play(checkMove, move)
|
|
72
111
|
} else {
|
|
73
|
-
|
|
112
|
+
trax.play(move)
|
|
74
113
|
}
|
|
75
114
|
|
|
76
|
-
if (
|
|
115
|
+
if (trax.move === moveNumber) {
|
|
77
116
|
CLI.error(
|
|
78
117
|
'Move "' +
|
|
79
118
|
(checkMove ? checkMove + '. ' : '') +
|
|
@@ -88,9 +127,17 @@ playCmd.fn = (CLI, ...moves) => {
|
|
|
88
127
|
}
|
|
89
128
|
}
|
|
90
129
|
|
|
91
|
-
if (
|
|
130
|
+
if (trax.move !== start) {
|
|
131
|
+
const result = bot(CLI)
|
|
92
132
|
CLI.updateGameData()
|
|
93
133
|
CLI.do('view')
|
|
134
|
+
if (result === 'lose') {
|
|
135
|
+
CLI.error('Failed to complete puzzle ' + game.puzzle)
|
|
136
|
+
} else if (result === 'win') {
|
|
137
|
+
CLI.out(CLI.color.success('You completed puzzle ' + game.puzzle))
|
|
138
|
+
} else if (result) {
|
|
139
|
+
CLI.out(result)
|
|
140
|
+
}
|
|
94
141
|
}
|
|
95
142
|
}
|
|
96
143
|
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/** @file Puzzles CLI
|
|
2
|
+
* @copyright 2022
|
|
3
|
+
* @author Chad Transtrum <chad@transtrum.net>
|
|
4
|
+
* @license Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { Trax } from '@slugbugblue/trax'
|
|
8
|
+
import { levels, puzzles, sources } from '@slugbugblue/trax/puzzles'
|
|
9
|
+
|
|
10
|
+
let maxLevel = 0
|
|
11
|
+
const puzzleIds = []
|
|
12
|
+
for (const puzzle of puzzles) {
|
|
13
|
+
maxLevel = Math.max(maxLevel, puzzle.level)
|
|
14
|
+
puzzleIds.push(puzzle.id)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const validLevels = levels.slice(0, maxLevel + 1)
|
|
18
|
+
const completions = [...validLevels, ...puzzleIds].join('|')
|
|
19
|
+
|
|
20
|
+
export const puzzlesCmd = {
|
|
21
|
+
name: 'puzzles',
|
|
22
|
+
args: '["list"|"start"] [level|puzzle]',
|
|
23
|
+
comp: 'list|ls|new|show|start|view ' + completions,
|
|
24
|
+
desc: 'play puzzles',
|
|
25
|
+
help: [
|
|
26
|
+
'Practice your skills with puzzles. Use "list" to see the puzzle',
|
|
27
|
+
'categories, or "start" to play a puzzle.',
|
|
28
|
+
],
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const an = (word = '') => {
|
|
32
|
+
const a = /^[aeiou]/.test(word) ? 'an' : 'a'
|
|
33
|
+
return a + ' ' + word
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const showPuzzle = (CLI, id) => {
|
|
37
|
+
const puzzle = puzzles.find((p) => p.id === id)
|
|
38
|
+
if (!puzzle) return CLI.error(`Puzzle ${id} not found`)
|
|
39
|
+
const trax = new Trax(puzzle.game, puzzle.notation, 'cli')
|
|
40
|
+
const players = ['white', 'black']
|
|
41
|
+
CLI.display(trax, players)
|
|
42
|
+
let out = CLI.color.short(puzzle.id)
|
|
43
|
+
out += CLI.color(` is ${an(levels[puzzle.level])} puzzle`) + '\n'
|
|
44
|
+
if (puzzle.title) out += CLI.color(puzzle.title) + '\n'
|
|
45
|
+
if (puzzle.desc) out += CLI.color.help(puzzle.desc) + '\n'
|
|
46
|
+
out += CLI.color(players[puzzle.player - 1] + ' to win by move ' + puzzle.max)
|
|
47
|
+
const src = sources[puzzle.src]
|
|
48
|
+
if (src) {
|
|
49
|
+
let source = '\nPuzzle '
|
|
50
|
+
source += src.copyright
|
|
51
|
+
? 'copyright ' + src.copyright + ' by'
|
|
52
|
+
: 'courtesy of'
|
|
53
|
+
source += ' ' + src.name
|
|
54
|
+
const url = src.url ? ' ' + CLI.color.white(src.url) : ''
|
|
55
|
+
out += CLI.color.black(source) + url
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
CLI.out(out)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const listPuzzles = (CLI) => {
|
|
62
|
+
let size = 1
|
|
63
|
+
const data = []
|
|
64
|
+
for (const name of validLevels) {
|
|
65
|
+
size = Math.max(size, name.length)
|
|
66
|
+
data.push({ name, count: 0, solved: 0, selected: false })
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (data[CLI.puzzleLevel]) data[CLI.puzzleLevel].selected = true
|
|
70
|
+
|
|
71
|
+
for (const puzzle of puzzles) {
|
|
72
|
+
data[puzzle.level].count += 1
|
|
73
|
+
if (CLI.puzzle(puzzle.id).solved > 0) data[puzzle.level].solved += 1
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
for (const category of data) {
|
|
77
|
+
let out = category.selected ? CLI.color('* ') : ' '
|
|
78
|
+
out += CLI.color.short(category.name.padEnd(size + 1))
|
|
79
|
+
if (category.solved === 0) {
|
|
80
|
+
out += CLI.color(CLI.plural(category.count, 'puzzle'))
|
|
81
|
+
} else if (category.solved === category.count) {
|
|
82
|
+
if (category.count > 3) out += CLI.color.success('all ')
|
|
83
|
+
out += CLI.color.success(CLI.plural(category.count, 'puzzle') + ' solved')
|
|
84
|
+
} else {
|
|
85
|
+
out += CLI.color.success(`${category.solved} solved`)
|
|
86
|
+
out += CLI.color(` of ${category.count} puzzles`)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
CLI.out(out)
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const listLevel = (CLI, levelName) => {
|
|
94
|
+
let size = 1
|
|
95
|
+
const level = levels.indexOf(levelName)
|
|
96
|
+
const data = []
|
|
97
|
+
for (const puzzle of puzzles) {
|
|
98
|
+
if (puzzle.level !== level) continue
|
|
99
|
+
size = Math.max(size, puzzle.id.length)
|
|
100
|
+
data.push({
|
|
101
|
+
id: puzzle.id,
|
|
102
|
+
title: puzzle.title,
|
|
103
|
+
solved: CLI.puzzle(puzzle.id).solved > 0,
|
|
104
|
+
})
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
for (const puzzle of data) {
|
|
108
|
+
let out = CLI.color.success(puzzle.solved ? '✓' : ' ')
|
|
109
|
+
out += CLI.color.short(' ' + puzzle.id)
|
|
110
|
+
if (puzzle.title) out += CLI.color(' ' + puzzle.title)
|
|
111
|
+
CLI.out(out)
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const startPuzzle = (CLI, puzzle) => {
|
|
116
|
+
const id = CLI.newPuzzle(puzzle)
|
|
117
|
+
CLI.out(CLI.color(`Started puzzle ${puzzle.id} as game #${id}`))
|
|
118
|
+
showPuzzle(CLI, puzzle.id)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const startNextPuzzle = (CLI, level) => {
|
|
122
|
+
if (level === undefined) level = CLI.puzzleLevel
|
|
123
|
+
const unsolved = puzzles.filter((p) => !CLI.puzzle(p.id).solved)
|
|
124
|
+
if (unsolved.length === 0) {
|
|
125
|
+
CLI.out(CLI.success('You have already completed all the puzzles.'))
|
|
126
|
+
return CLI.error('Specify a specific puzzle to try one again.')
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const next = unsolved.find((p) => p.level >= level) || unsolved[0]
|
|
130
|
+
startPuzzle(CLI, next)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
puzzlesCmd.fn = (CLI, action, id) => {
|
|
134
|
+
action = action?.toLowerCase() ?? 'list'
|
|
135
|
+
if ('start'.startsWith(action) || 'new'.startsWith(action)) {
|
|
136
|
+
if (!id) return startNextPuzzle(CLI)
|
|
137
|
+
|
|
138
|
+
const puzzle = puzzles.find((p) => p.id === id)
|
|
139
|
+
if (puzzle) {
|
|
140
|
+
return startPuzzle(CLI, puzzle)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (validLevels.includes(id)) {
|
|
144
|
+
return startNextPuzzle(CLI, levels.indexOf(id))
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return CLI.error('Could not start puzzle ' + id)
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (action) {
|
|
151
|
+
const x = id || action
|
|
152
|
+
if (validLevels.includes(x)) return listLevel(CLI, x)
|
|
153
|
+
if (puzzleIds.includes(x)) return showPuzzle(CLI, x)
|
|
154
|
+
if (id) return CLI.error('Could not find puzzle ' + id)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
listPuzzles(CLI)
|
|
158
|
+
}
|
package/src/cmds/suggest.js
CHANGED
package/src/cmds/view.js
CHANGED
package/src/puzzles.js
CHANGED
|
@@ -4,10 +4,20 @@
|
|
|
4
4
|
* @license Apache-2.0
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
/** @type string[] */
|
|
8
|
+
export const levels = [
|
|
9
|
+
'tutorial',
|
|
10
|
+
'beginner',
|
|
11
|
+
'tough',
|
|
12
|
+
'advanced',
|
|
13
|
+
'expert',
|
|
14
|
+
'master',
|
|
15
|
+
]
|
|
16
|
+
|
|
7
17
|
/** @type {{[key: string]: PuzzleSource}} */
|
|
8
18
|
export const sources = {
|
|
9
19
|
sbb: {
|
|
10
|
-
name: 'puzzlebot
|
|
20
|
+
name: "slugbugblue's puzzlebot",
|
|
11
21
|
},
|
|
12
22
|
traxgame: {
|
|
13
23
|
name: 'Donald G. Bailey',
|
|
@@ -25,8 +35,8 @@ export const puzzles = [
|
|
|
25
35
|
icon: 'fd',
|
|
26
36
|
player: 1,
|
|
27
37
|
max: 3,
|
|
28
|
-
level:
|
|
29
|
-
title: '
|
|
38
|
+
level: 0,
|
|
39
|
+
title: 'Loop attack',
|
|
30
40
|
desc: "You're playing white, so when you form a white loop, you win the game.",
|
|
31
41
|
hints: ['A0/', 'B0\\'],
|
|
32
42
|
},
|
|
@@ -38,8 +48,8 @@ export const puzzles = [
|
|
|
38
48
|
icon: 'def',
|
|
39
49
|
player: 2,
|
|
40
50
|
max: 4,
|
|
41
|
-
level:
|
|
42
|
-
title: '
|
|
51
|
+
level: 0,
|
|
52
|
+
title: 'Wide loop attack',
|
|
43
53
|
desc: "Your loop doesn't have to be a perfect circle, and it can be oriented in any direction.",
|
|
44
54
|
hint: 'B2+',
|
|
45
55
|
},
|
|
@@ -51,10 +61,10 @@ export const puzzles = [
|
|
|
51
61
|
icon: 'ca:0e:0d',
|
|
52
62
|
player: 1,
|
|
53
63
|
max: 5,
|
|
54
|
-
level:
|
|
64
|
+
level: 0,
|
|
55
65
|
title: 'Partially formed wide loop',
|
|
56
66
|
desc: "It doesn't matter which direction the loops are facing.",
|
|
57
|
-
|
|
67
|
+
hints: ['A2+', 'A3\\'],
|
|
58
68
|
},
|
|
59
69
|
{
|
|
60
70
|
id: 'tutorial.04',
|
|
@@ -64,7 +74,7 @@ export const puzzles = [
|
|
|
64
74
|
icon: 'd:aeec',
|
|
65
75
|
player: 2,
|
|
66
76
|
max: 6,
|
|
67
|
-
level:
|
|
77
|
+
level: 0,
|
|
68
78
|
title: 'Using a hollow',
|
|
69
79
|
desc: 'You can use forced tiles with an inside corner to stretch your attack.',
|
|
70
80
|
hint: 'C1+',
|
|
@@ -77,7 +87,7 @@ export const puzzles = [
|
|
|
77
87
|
icon: 'ca:0ef:0ec:0d',
|
|
78
88
|
player: 1,
|
|
79
89
|
max: 7,
|
|
80
|
-
level:
|
|
90
|
+
level: 0,
|
|
81
91
|
title: 'Diverted attack',
|
|
82
92
|
desc: "Even though black is attacking into this area, it doesn't stop you from completing your loop.",
|
|
83
93
|
hint: 'A3+',
|
|
@@ -90,7 +100,7 @@ export const puzzles = [
|
|
|
90
100
|
icon: 'd2f:aeeec',
|
|
91
101
|
player: 2,
|
|
92
102
|
max: 8,
|
|
93
|
-
level:
|
|
103
|
+
level: 0,
|
|
94
104
|
title: 'Long cave',
|
|
95
105
|
desc: 'Stretch the line with forced tiles.',
|
|
96
106
|
hint: 'C1+',
|
|
@@ -103,7 +113,7 @@ export const puzzles = [
|
|
|
103
113
|
icon: 'ae:0f:0a',
|
|
104
114
|
player: 1,
|
|
105
115
|
max: 7,
|
|
106
|
-
level:
|
|
116
|
+
level: 0,
|
|
107
117
|
title: 'Introduction to the L',
|
|
108
118
|
desc: 'Use an L to form two attacks at once.',
|
|
109
119
|
hint: 'A3/',
|
|
@@ -116,7 +126,7 @@ export const puzzles = [
|
|
|
116
126
|
icon: 'f:bae:1f',
|
|
117
127
|
player: 2,
|
|
118
128
|
max: 8,
|
|
119
|
-
level:
|
|
129
|
+
level: 0,
|
|
120
130
|
title: 'Mirrored Ls',
|
|
121
131
|
desc: "This mirrored position gives an L to both players, but since it's your turn, you have the initiative.",
|
|
122
132
|
hint: 'A3/',
|
|
@@ -129,7 +139,7 @@ export const puzzles = [
|
|
|
129
139
|
icon: 'a:efae',
|
|
130
140
|
player: 2,
|
|
131
141
|
max: 8,
|
|
132
|
-
level:
|
|
142
|
+
level: 0,
|
|
133
143
|
title: 'Sideways L',
|
|
134
144
|
desc: 'An L can be in any orientation.',
|
|
135
145
|
hint: 'D1+',
|
|
@@ -142,7 +152,7 @@ export const puzzles = [
|
|
|
142
152
|
icon: 'fae:1ef',
|
|
143
153
|
player: 2,
|
|
144
154
|
max: 8,
|
|
145
|
-
level:
|
|
155
|
+
level: 0,
|
|
146
156
|
title: 'Ls come in many shapes',
|
|
147
157
|
hint: 'A2/',
|
|
148
158
|
},
|
|
@@ -150,13 +160,13 @@ export const puzzles = [
|
|
|
150
160
|
id: 'tutorial.11',
|
|
151
161
|
src: 'sbb',
|
|
152
162
|
game: 'trax',
|
|
153
|
-
notation: '@0+
|
|
163
|
+
notation: '@0+ B1/ C1/ @1+ A2/',
|
|
154
164
|
icon: 'faee:2f',
|
|
155
165
|
player: 2,
|
|
156
166
|
max: 8,
|
|
157
|
-
level:
|
|
167
|
+
level: 0,
|
|
158
168
|
title: 'Another L',
|
|
159
|
-
hint: '
|
|
169
|
+
hint: 'D2\\',
|
|
160
170
|
},
|
|
161
171
|
{
|
|
162
172
|
id: 'tutorial.12',
|
|
@@ -166,7 +176,7 @@ export const puzzles = [
|
|
|
166
176
|
icon: 'ae:fd:0ae',
|
|
167
177
|
player: 2,
|
|
168
178
|
max: 8,
|
|
169
|
-
level:
|
|
179
|
+
level: 0,
|
|
170
180
|
title: 'Fat L',
|
|
171
181
|
desc: "Even though this looks a bit odd, it's just another L.",
|
|
172
182
|
hint: 'C1/',
|
|
@@ -179,7 +189,7 @@ export const puzzles = [
|
|
|
179
189
|
icon: 'bbae:2f',
|
|
180
190
|
player: 2,
|
|
181
191
|
max: 8,
|
|
182
|
-
level:
|
|
192
|
+
level: 0,
|
|
183
193
|
title: 'Connectable pair L',
|
|
184
194
|
desc: 'This L uses a connectable pair instead of two corners, but it works the same as the other Ls.',
|
|
185
195
|
hints: ['A2\\', 'B2/'],
|
|
@@ -188,14 +198,14 @@ export const puzzles = [
|
|
|
188
198
|
id: 'tutorial.14',
|
|
189
199
|
src: 'sbb',
|
|
190
200
|
game: 'trax',
|
|
191
|
-
notation: '@0+ @1
|
|
201
|
+
notation: '@0+ @1/ @1/ C0+ C0+',
|
|
192
202
|
icon: 'fae:1e:1e',
|
|
193
203
|
player: 2,
|
|
194
204
|
max: 8,
|
|
195
|
-
level:
|
|
205
|
+
level: 0,
|
|
196
206
|
title: 'Another connectable pair L',
|
|
197
207
|
desc: 'There is a lot of variation in how Ls look, but being able to recognize them and activate them is key.',
|
|
198
|
-
hint: 'A2
|
|
208
|
+
hint: 'A2\\',
|
|
199
209
|
},
|
|
200
210
|
{
|
|
201
211
|
id: 'tutorial.15',
|
|
@@ -205,7 +215,7 @@ export const puzzles = [
|
|
|
205
215
|
icon: 'e:e:d:ae',
|
|
206
216
|
player: 2,
|
|
207
217
|
max: 8,
|
|
208
|
-
level:
|
|
218
|
+
level: 0,
|
|
209
219
|
title: 'Simple connectable pair L',
|
|
210
220
|
desc: 'Starting to get the hang of this?',
|
|
211
221
|
hints: ['B1\\', 'B2/'],
|
|
@@ -218,7 +228,7 @@ export const puzzles = [
|
|
|
218
228
|
icon: 'a:e:eec',
|
|
219
229
|
player: 2,
|
|
220
230
|
max: 8,
|
|
221
|
-
level:
|
|
231
|
+
level: 0,
|
|
222
232
|
title: 'Strange L',
|
|
223
233
|
desc: 'Just another connectable pair L.',
|
|
224
234
|
hint: 'B1/',
|
|
@@ -430,7 +440,7 @@ export const puzzles = [
|
|
|
430
440
|
level: 2,
|
|
431
441
|
player: 1,
|
|
432
442
|
max: 11,
|
|
433
|
-
|
|
443
|
+
hints: ['A0/', 'C4\\', 'D5/'],
|
|
434
444
|
},
|
|
435
445
|
{
|
|
436
446
|
id: 'iw3.trx',
|
|
@@ -441,7 +451,7 @@ export const puzzles = [
|
|
|
441
451
|
level: 2,
|
|
442
452
|
player: 1,
|
|
443
453
|
max: 11,
|
|
444
|
-
|
|
454
|
+
hints: ['B0/', 'C0\\'],
|
|
445
455
|
},
|
|
446
456
|
{
|
|
447
457
|
id: 'iw4.trx',
|
|
@@ -452,7 +462,7 @@ export const puzzles = [
|
|
|
452
462
|
level: 2,
|
|
453
463
|
player: 1,
|
|
454
464
|
max: 11,
|
|
455
|
-
|
|
465
|
+
hints: ['@2/', 'C0/'],
|
|
456
466
|
},
|
|
457
467
|
{
|
|
458
468
|
id: 'iw5.trx',
|
|
@@ -485,7 +495,7 @@ export const puzzles = [
|
|
|
485
495
|
level: 2,
|
|
486
496
|
player: 1,
|
|
487
497
|
max: 13,
|
|
488
|
-
|
|
498
|
+
hints: ['A1+', 'D1/'],
|
|
489
499
|
},
|
|
490
500
|
{
|
|
491
501
|
id: 'ib1.trx',
|
|
@@ -507,7 +517,7 @@ export const puzzles = [
|
|
|
507
517
|
level: 2,
|
|
508
518
|
player: 2,
|
|
509
519
|
max: 12,
|
|
510
|
-
|
|
520
|
+
hints: ['A1\\', 'E1/'],
|
|
511
521
|
},
|
|
512
522
|
{
|
|
513
523
|
id: 'ib3.trx',
|
|
@@ -529,7 +539,7 @@ export const puzzles = [
|
|
|
529
539
|
level: 2,
|
|
530
540
|
player: 2,
|
|
531
541
|
max: 12,
|
|
532
|
-
|
|
542
|
+
hints: ['B0/', 'C0+'],
|
|
533
543
|
},
|
|
534
544
|
{
|
|
535
545
|
id: 'ib5.trx',
|
|
@@ -562,7 +572,7 @@ export const puzzles = [
|
|
|
562
572
|
level: 2,
|
|
563
573
|
player: 2,
|
|
564
574
|
max: 10,
|
|
565
|
-
|
|
575
|
+
hints: ['A0/', 'D3/'],
|
|
566
576
|
},
|
|
567
577
|
{
|
|
568
578
|
id: 'ib8.trx',
|
|
@@ -638,8 +648,8 @@ export const puzzles = [
|
|
|
638
648
|
icon: '0ca:deef:0d',
|
|
639
649
|
level: 2,
|
|
640
650
|
player: 1,
|
|
641
|
-
max:
|
|
642
|
-
|
|
651
|
+
max: 15,
|
|
652
|
+
hints: ['B0/', 'C0\\'],
|
|
643
653
|
},
|
|
644
654
|
{
|
|
645
655
|
id: 'hw4.trx',
|
|
@@ -737,8 +747,8 @@ export const puzzles = [
|
|
|
737
747
|
icon: 'd:b:ba:aeecd:0fdec',
|
|
738
748
|
level: 1,
|
|
739
749
|
player: 2,
|
|
740
|
-
max:
|
|
741
|
-
|
|
750
|
+
max: 18,
|
|
751
|
+
hints: ['B1+', 'B2/', 'D3+'],
|
|
742
752
|
},
|
|
743
753
|
{
|
|
744
754
|
id: 'hb3.trx',
|
|
@@ -757,7 +767,7 @@ export const puzzles = [
|
|
|
757
767
|
game: 'traxloop',
|
|
758
768
|
notation: '@0+ A0/ A0/ A4/ A5+ @5/ @5/ @5+ A4/ @5/',
|
|
759
769
|
icon: '3d:3c:3e:0d1d:cbdcb',
|
|
760
|
-
level:
|
|
770
|
+
level: 3,
|
|
761
771
|
player: 1,
|
|
762
772
|
max: 15,
|
|
763
773
|
hint: 'D1\\',
|
|
@@ -768,7 +778,7 @@ export const puzzles = [
|
|
|
768
778
|
game: 'traxloop',
|
|
769
779
|
notation: '@0+ B1/ C1+ C0/ D1\\ E1\\ F1+ E0/ F1/',
|
|
770
780
|
icon: '2cdc:1dfae:ecb',
|
|
771
|
-
level:
|
|
781
|
+
level: 3,
|
|
772
782
|
player: 2,
|
|
773
783
|
max: 16,
|
|
774
784
|
hint: 'F0/',
|
|
@@ -779,7 +789,7 @@ export const puzzles = [
|
|
|
779
789
|
game: 'traxloop',
|
|
780
790
|
notation: '@0/ @1+ A0/ A0/ B2+ @2\\ @2\\ E3\\ E4+',
|
|
781
791
|
icon: '1ca:afde:1bdf:3b',
|
|
782
|
-
level:
|
|
792
|
+
level: 3,
|
|
783
793
|
player: 2,
|
|
784
794
|
max: 16,
|
|
785
795
|
hint: 'A1\\',
|
|
@@ -790,9 +800,13 @@ export const puzzles = [
|
|
|
790
800
|
game: 'traxloop',
|
|
791
801
|
notation: '@0/ B1+ C1/ C0/ C0\\ @3/ C1+ E2/ F2\\',
|
|
792
802
|
icon: '1baf:1cdca:cdecd',
|
|
793
|
-
level:
|
|
803
|
+
level: 3,
|
|
794
804
|
player: 2,
|
|
795
805
|
max: 16,
|
|
796
806
|
hint: 'B1+',
|
|
797
807
|
},
|
|
798
808
|
]
|
|
809
|
+
|
|
810
|
+
puzzles.sort(
|
|
811
|
+
(a, b) => a.level - b.level || a.max - b.max || (a.id < b.id ? -1 : 1),
|
|
812
|
+
)
|