Game-Polish #10

Merged
brianfertig merged 2 commits from Game-Polish into main 2026-09-02 05:10:18 +00:00
6 changed files with 28 additions and 8 deletions
Showing only changes of commit 44de2fb049 - Show all commits

View File

@ -88,7 +88,7 @@ menu categories:
| **Tabletop** | 21 | Backgammon, Chess, Checkers, Go, Othello, Settlers of Catan, Ticket to Ride, Risk, Monopoly, Blokus, Labyrinth, Mahjong, Stratego, Battleship, Mastermind, Connect 4, Forbidden Island, Azul, Chinese Checkers, Mexican Train, Parchisi | | **Tabletop** | 21 | Backgammon, Chess, Checkers, Go, Othello, Settlers of Catan, Ticket to Ride, Risk, Monopoly, Blokus, Labyrinth, Mahjong, Stratego, Battleship, Mastermind, Connect 4, Forbidden Island, Azul, Chinese Checkers, Mexican Train, Parchisi |
| **Cards** | 18 | Cribbage, Gin Rummy, Rummikub, Canasta, Hearts, Uno, Phase 10, Skip-Bo, Go Fish, Old Maid, Nerts, Dominion, Splendor, Freecell, Solitaire Tour, Spire Climb, Zahtzee, Farkle | | **Cards** | 18 | Cribbage, Gin Rummy, Rummikub, Canasta, Hearts, Uno, Phase 10, Skip-Bo, Go Fish, Old Maid, Nerts, Dominion, Splendor, Freecell, Solitaire Tour, Spire Climb, Zahtzee, Farkle |
| **Casino** | 9 | Blackjack, Texas Hold 'Em, Baccarat, Pai Gow Poker, Video Poker, Craps, Roulette, Bingo, Slot Machines | | **Casino** | 9 | Blackjack, Texas Hold 'Em, Baccarat, Pai Gow Poker, Video Poker, Craps, Roulette, Bingo, Slot Machines |
| **Word** | 15 | Wordle Race, Scrabble, Boggle, Ghost, Word Ladder, Word Search, Hangman, Spelling Bee, Sudoku, Mini Crossword, Tectonic, Bookwork, Kiitos, Tri-Ominoes, Jumble | | **Word** | 15 | Wordle Race, Scrabble, Boggle, Ghost, Word Ladder, Word Search, Hangman, Spelling Bee, Sudoku, Mini Crossword, Tectonic, Bookworm, Kiitos, Tri-Ominoes, Jumble |
| **Logic & Puzzle** | 14 | 2048, Rush Hour, Hexsweeper, Jell-o Monsters, Shift, Mahjong Match, Jewel Quest, Zuma, Bejeweled Blitz, Mini Motorways, Dot Link, Katamino, Genius Square, Block Fighter | | **Logic & Puzzle** | 14 | 2048, Rush Hour, Hexsweeper, Jell-o Monsters, Shift, Mahjong Match, Jewel Quest, Zuma, Bejeweled Blitz, Mini Motorways, Dot Link, Katamino, Genius Square, Block Fighter |
| **Arcade, Console & PC** | 2 | Colorado Defense, Star Control | | **Arcade, Console & PC** | 2 | Colorado Defense, Star Control |

View File

@ -1,5 +1,12 @@
{ {
"playerBaseHp": 100, "playerBaseHp": 100,
"steer": {
"wordBoostK": 1.0,
"classThreshold": 2,
"classDamp": 0.35,
"classBoost": 1.8,
"vowelBand": [0.30, 0.45]
},
"milestones": [ "milestones": [
{ "afterLevel": 5, "maxHpBonus": 10, "unlock": "potion" }, { "afterLevel": 5, "maxHpBonus": 10, "unlock": "potion" },
{ "afterLevel": 10, "maxHpBonus": 10 }, { "afterLevel": 10, "maxHpBonus": 10 },

View File

@ -99,7 +99,7 @@ registerGame({ slug: 'ginrummy', name: 'Gin Rummy', category: 'cards', cardGame:
registerGame({ slug: 'risk', name: 'Risk', category: 'tabletop', minPlayers: 2, maxPlayers: 6, minOpponents: 1, maxOpponents: 5, defaultOpponents: 3, hasTutorial: true, iconFrame: 54, defaultPlayfield: 'combat' }); registerGame({ slug: 'risk', name: 'Risk', category: 'tabletop', minPlayers: 2, maxPlayers: 6, minOpponents: 1, maxOpponents: 5, defaultOpponents: 3, hasTutorial: true, iconFrame: 54, defaultPlayfield: 'combat' });
registerGame({ slug: 'geniussquare', name: 'Genius Square', category: 'logic', minPlayers: 1, maxPlayers: 2, minOpponents: 1, maxOpponents: 1, iconFrame: 70 }); registerGame({ slug: 'geniussquare', name: 'Genius Square', category: 'logic', minPlayers: 1, maxPlayers: 2, minOpponents: 1, maxOpponents: 1, iconFrame: 70 });
registerGame({ slug: 'katamino', name: 'Katamino', category: 'logic', minPlayers: 1, maxPlayers: 1, minOpponents: 0, maxOpponents: 0, iconFrame: 71 }); registerGame({ slug: 'katamino', name: 'Katamino', category: 'logic', minPlayers: 1, maxPlayers: 1, minOpponents: 0, maxOpponents: 0, iconFrame: 71 });
registerGame({ slug: 'bookwork', name: 'Bookwork', category: 'word', minPlayers: 1, maxPlayers: 1, minOpponents: 0, maxOpponents: 0, iconFrame: 72 }); registerGame({ slug: 'bookwork', name: 'Bookworm', category: 'word', minPlayers: 1, maxPlayers: 1, minOpponents: 0, maxOpponents: 0, iconFrame: 72 });
registerGame({ slug: 'paigow', name: 'Pai Gow Poker', category: 'casino', cardGame: true, minPlayers: 1, maxPlayers: 5, minOpponents: 0, maxOpponents: 4, defaultOpponents: 4, iconFrame: 73 }); registerGame({ slug: 'paigow', name: 'Pai Gow Poker', category: 'casino', cardGame: true, minPlayers: 1, maxPlayers: 5, minOpponents: 0, maxOpponents: 4, defaultOpponents: 4, iconFrame: 73 });
registerGame({ slug: 'spireclimb', name: 'Spire Climb', category: 'cards', cardGame: true, minPlayers: 1, maxPlayers: 1, minOpponents: 0, maxOpponents: 0, hasTutorial: true, iconFrame: 74 }); registerGame({ slug: 'spireclimb', name: 'Spire Climb', category: 'cards', cardGame: true, minPlayers: 1, maxPlayers: 1, minOpponents: 0, maxOpponents: 0, hasTutorial: true, iconFrame: 74 });
registerGame({ slug: 'azul', name: 'Azul', category: 'tabletop', minPlayers: 2, maxPlayers: 4, minOpponents: 1, maxOpponents: 3, defaultOpponents: 3, hasTutorial: true, iconFrame: 75, defaultPlayfield: 'cherry' }); registerGame({ slug: 'azul', name: 'Azul', category: 'tabletop', minPlayers: 2, maxPlayers: 4, minOpponents: 1, maxOpponents: 3, defaultOpponents: 3, hasTutorial: true, iconFrame: 75, defaultPlayfield: 'cherry' });

View File

@ -12,6 +12,7 @@ import {
computeMaxHp, isPotionUnlocked, computeMaxHp, isPotionUnlocked,
} from './BookworkLogic.js'; } from './BookworkLogic.js';
import { getAttackDamage, getSpecialTile } from './BookworkAI.js'; import { getAttackDamage, getSpecialTile } from './BookworkAI.js';
import { makeSteeredGrid, refillSteered, parseWordList } from './BookworkSteering.js';
const CELL = 96; const CELL = 96;
const GRID_W = CELL * GRID_SIZE; const GRID_W = CELL * GRID_SIZE;
@ -39,7 +40,7 @@ export default class BookworkGame extends Phaser.Scene {
constructor() { super('BookworkGame'); } constructor() { super('BookworkGame'); }
init(data) { init(data) {
this.gameDef = data.game ?? { slug: 'bookwork', name: 'Bookwork' }; this.gameDef = data.game ?? { slug: 'bookwork', name: 'Bookworm' };
this.config = { playerBaseHp: 100, milestones: [], levels: [] }; this.config = { playerBaseHp: 100, milestones: [], levels: [] };
this.bank = []; this.bank = [];
this.roster = []; this.roster = [];
@ -56,6 +57,8 @@ export default class BookworkGame extends Phaser.Scene {
this.bgTex = null; this.bgTex = null;
// Battle state // Battle state
this.grid = null; this.grid = null;
this.wordSet = null; // steering dictionary (ENABLE words 315); null → unsteered
this.steerOpts = {};
this.tileObjs = null; this.tileObjs = null;
this.selection = []; this.selection = [];
this.selGraphics = null; this.selGraphics = null;
@ -85,8 +88,16 @@ export default class BookworkGame extends Phaser.Scene {
const raw = this.cache.json.get('bookwork'); const raw = this.cache.json.get('bookwork');
if (raw) this.config = raw; if (raw) this.config = raw;
this.steerOpts = this.config.steer ?? {};
this.bank = (this.config.levels ?? []).slice().sort((a, b) => a.level - b.level); this.bank = (this.config.levels ?? []).slice().sort((a, b) => a.level - b.level);
// Load the steering dictionary (same ENABLE list the word validator uses).
// Non-fatal: if it fails, boards fall back to plain weighted-random.
try {
const res = await fetch('data/wordlists/enable1.txt');
if (res.ok) this.wordSet = parseWordList(await res.text());
} catch (_) { this.wordSet = null; }
try { try {
const res = await fetch('data/opponents.json'); const res = await fetch('data/opponents.json');
const json = await res.json(); const json = await res.json();
@ -279,7 +290,7 @@ export default class BookworkGame extends Phaser.Scene {
this.clearLayer(); this.clearLayer();
const cx = GAME_WIDTH / 2; const cx = GAME_WIDTH / 2;
const title = this.add.text(cx, 84, 'BOOKWORK', { const title = this.add.text(cx, 84, 'BOOKWORM', {
fontFamily: 'Righteous', fontSize: '64px', color: COLORS.goldHex, fontFamily: 'Righteous', fontSize: '64px', color: COLORS.goldHex,
}).setOrigin(0.5); }).setOrigin(0.5);
const sub = this.add.text(cx, 138, 'Spell words from the letter grid to battle your way through 20 opponents.', { const sub = this.add.text(cx, 138, 'Spell words from the letter grid to battle your way through 20 opponents.', {
@ -523,7 +534,7 @@ export default class BookworkGame extends Phaser.Scene {
this.potionUnlocked = isPotionUnlocked(this.config, this.levelsCompleted); this.potionUnlocked = isPotionUnlocked(this.config, this.levelsCompleted);
this.potionUsed = false; this.potionUsed = false;
this.turnPhase = 'player'; this.turnPhase = 'player';
this.grid = makeGrid(); this.grid = this.wordSet ? makeSteeredGrid(Math.random, this.wordSet, this.steerOpts) : makeGrid();
this.selection = []; this.selection = [];
this.drawBattleUI(); this.drawBattleUI();
@ -832,7 +843,9 @@ export default class BookworkGame extends Phaser.Scene {
} }
// Refill grid // Refill grid
this.grid = clearAndRefill(this.grid, cells); this.grid = this.wordSet
? refillSteered(this.grid, cells, Math.random, this.wordSet, this.steerOpts)
: clearAndRefill(this.grid, cells);
await this.animateRefill(cells); await this.animateRefill(cells);
this.redrawAllTiles(); this.redrawAllTiles();

View File

@ -1,7 +1,7 @@
export const GRID_SIZE = 5; export const GRID_SIZE = 5;
// Weighted letter pool tuned for playability (vowel-rich, rare letters suppressed) // Weighted letter pool tuned for playability (vowel-rich, rare letters suppressed)
const LETTER_WEIGHTS = { export const LETTER_WEIGHTS = {
A:9, B:2, C:3, D:4, E:13, F:2, G:2, H:3, I:8, J:1, K:2, A:9, B:2, C:3, D:4, E:13, F:2, G:2, H:3, I:8, J:1, K:2,
L:4, M:3, N:6, O:7, P:2, R:6, S:5, T:7, U:4, V:2, W:2, L:4, M:3, N:6, O:7, P:2, R:6, S:5, T:7, U:4, V:2, W:2,
X:1, Y:3, Z:1, X:1, Y:3, Z:1,

View File

@ -1,5 +1,5 @@
#!/usr/bin/env node #!/usr/bin/env node
// verifyBookwork.js — engine tests for Bookwork // verifyBookwork.js — engine tests for Bookworm
import { readFileSync } from 'node:fs'; import { readFileSync } from 'node:fs';
import { import {