diff --git a/frontend/src/js/fight_scene.ts b/frontend/src/js/fight_scene.ts index 8c1f33fb0f7d2b666415f74bd7d0e9bceb70a1fe..2bb01d0afbaf6afc819aab8dc7abff2a5eae10c8 100644 --- a/frontend/src/js/fight_scene.ts +++ b/frontend/src/js/fight_scene.ts @@ -1,17 +1,16 @@ import "phaser"; -import Critter from "./critter"; import Spear from "./spear"; import backend from "./backend"; // TODO: write interfaces import levenshtein from "damerau-levenshtein"; -import Clue from "./clue"; import * as Types from "../../../backend/src/types"; +import Foe from "./foe"; export default class FightScene extends Phaser.Scene { - foes: Array<Critter>; + foes: Array<Foe>; ground: Phaser.Types.Physics.Arcade.ImageWithStaticBody; player: Phaser.Types.Physics.Arcade.SpriteWithDynamicBody; cluesGroup: Phaser.Physics.Arcade.Group; @@ -197,18 +196,18 @@ export default class FightScene extends Phaser.Scene { }); } - shootSpear(enemy: Critter, hit: boolean) { + shootSpear(foe: Foe | null, hit: boolean) { const scene = this; - if (!hit) { + if (foe === null || !hit) { this.showMissMessage(); + new Spear(this, this.player, undefined); } else { this.showHitMessage(); // TODO: ew. - enemy.clue.delete(); - scene.foes.splice(scene.foes.indexOf(enemy), 1); // FIXME + foe.clue.delete(); + // scene.foes.splice(scene.foes.indexOf(foe), 1); // FIXME + new Spear(this, this.player, foe.critter); } - - new Spear(this, this.player, hit ? enemy : undefined); } initCluesGroup() { @@ -226,6 +225,25 @@ export default class FightScene extends Phaser.Scene { }); this.physics.add.collider(this.cluesGroup, this.cluesGroup); } + + findMatchingFoe(transcription: string) { + let result: { score: number; match: Foe | null } = { + score: -1, + match: null, + }; + if (this.foes.length < 1) return result; + this.foes.forEach((foe) => { + const similarity = levenshtein( + transcription.toLowerCase(), + foe.beWord.ocr_transcript.toLowerCase(), + ).similarity; + if (similarity < result.score) return; + result = { score: similarity, match: foe }; + }); + // match ??= scene.foes[0]; // TODO: remove this + // console.log(similarity, match.beWord.ocr_transcript); + return result; + } } // TODO: remove any @@ -307,50 +325,22 @@ function initAndBindGuessPreview(scene: FightScene) { } function submitTranscription(transcription: string, scene: FightScene) { - if (scene.foes.length < 1) return; - - let similarity = 0; - let match = null; - - scene.foes.forEach((critter) => { - const s = levenshtein( - transcription.toLowerCase(), - critter.clue.word.ocr_transcript.toLowerCase(), - ).similarity; - if (s < similarity) return; - similarity = s; - match = critter; - }); - - match ??= scene.foes[0]; // TODO: remove this - - console.log(similarity, match.clue.word.ocr_transcript); - - // TODO: we can have near misses depending on similarity! - const hit = similarity >= 0.9; - const enemy = match; - - scene.shootSpear(enemy, hit); + const { score, match } = scene.findMatchingFoe(transcription); + scene.shootSpear(match, score >= 0.9); } function gameStart(scene: any) { spawn(scene); - // dispatchEnemy(scene); } -function spawn(scene: any) { - dispatchEnemy(scene); +async function spawn(scene: any) { + await dispatchEnemy(scene); scene.time.now; const delay = (8 * 1000 * (60 * 1000 - scene.time.now)) / 60 / 1000 + 2 * 1000; setTimeout(() => spawn(scene), Math.max(delay, 2000)); } -function dispatchEnemy(scene: any) { - backend.getWord().then(function (response) { - const clue = new Clue(scene, response.data); - const critter = new Critter(scene, clue); - // TODO: clue - scene.foes.push(critter); - }); +async function dispatchEnemy(scene: any) { + await new Foe(scene).initialize(); } diff --git a/frontend/src/js/foe.ts b/frontend/src/js/foe.ts new file mode 100644 index 0000000000000000000000000000000000000000..08abc869cd8ebeb1e51e36db5948e1ed58989a3d --- /dev/null +++ b/frontend/src/js/foe.ts @@ -0,0 +1,29 @@ +import "phaser"; +import Clue from "./clue"; +import Critter from "./critter"; +import FightScene from "./fight_scene"; + +import backend from "./backend"; +import * as Types from "../../../backend/src/types"; + +class Foe { + beWord: Types.Word; + beClue: Types.Clue; + + scene: FightScene; + critter: Critter; + clue: Clue; + + constructor(scene: FightScene) { + this.scene = scene; + } + + async initialize() { + this.beWord = (await backend.getWord()).data; + this.clue = new Clue(this.scene, this.beWord); + this.critter = new Critter(this.scene, this.clue); + this.scene.foes.push(this); + } +} + +export default Foe;