From cf2b555c2e6af5e4635026900dba7b7b7100f57c Mon Sep 17 00:00:00 2001 From: Paolo Brasolin <paolo.brasolin@eurac.edu> Date: Wed, 13 Apr 2022 15:46:09 +0200 Subject: [PATCH] feat: #be #fe track score and similarity --- .../migrations/20220413131534_extra_data.ts | 19 +++++++ backend/src/schemas.ts | 11 ++++- frontend/src/js/fight_scene.ts | 49 ++++++++++++------- 3 files changed, 60 insertions(+), 19 deletions(-) create mode 100644 backend/migrations/20220413131534_extra_data.ts diff --git a/backend/migrations/20220413131534_extra_data.ts b/backend/migrations/20220413131534_extra_data.ts new file mode 100644 index 0000000..ffd6c10 --- /dev/null +++ b/backend/migrations/20220413131534_extra_data.ts @@ -0,0 +1,19 @@ +import { Knex } from "knex"; + +export async function up(knex: Knex): Promise<void> { + return knex.schema + .alterTable("shots", (table) => { + table.integer("score"); + table.float("similarity").nullable(); + }) + .alterTable("games", (table) => table.integer("score").nullable()); +} + +export async function down(knex: Knex): Promise<void> { + return knex.schema + .alterTable("shots", (table) => { + table.dropColumn("score"); + table.dropColumn("similarity"); + }) + .alterTable("games", (table) => table.dropColumn("score")); +} diff --git a/backend/src/schemas.ts b/backend/src/schemas.ts index 87a6bd3..7af17f9 100644 --- a/backend/src/schemas.ts +++ b/backend/src/schemas.ts @@ -22,6 +22,7 @@ export const Game = Type.Object({ ended_at: Nullable(Type.String({ format: "date-time" })), began_at_gmtm: Nullable(Type.Number({ minimum: 0 })), ended_at_gmtm: Nullable(Type.Number({ minimum: 0 })), + score: Nullable(Type.Integer()), }); export const Clue = Type.Object({ @@ -44,10 +45,18 @@ export const Shot = Type.Object({ ended_at_gmtm: Nullable(Type.Number({ minimum: 0 })), typed: Type.String(), final: Type.String(), + score: Type.Integer(), + similarity: Type.Number(), }); export const GameUpdate = Type.Partial( - Type.Pick(Game, ["began_at", "ended_at", "began_at_gmtm", "ended_at_gmtm"]), + Type.Pick(Game, [ + "began_at", + "ended_at", + "began_at_gmtm", + "ended_at_gmtm", + "score", + ]), ); export const GameCreate = GameUpdate; diff --git a/frontend/src/js/fight_scene.ts b/frontend/src/js/fight_scene.ts index 0ea4f80..86b9409 100644 --- a/frontend/src/js/fight_scene.ts +++ b/frontend/src/js/fight_scene.ts @@ -352,6 +352,7 @@ export default class FightScene extends Phaser.Scene { await backend.updateGame(this.beGame.id, { ended_at: new Date().toISOString(), ended_at_gmtm: this.getGameTime(), + score: this.score, }) ).data; this.spawner.remove(); @@ -373,8 +374,8 @@ export default class FightScene extends Phaser.Scene { } findMatchingFoe(transcription: string) { - let result: { score: number; match: Foe | null } = { - score: -1, + let result: { similarity: number; match: Foe | null } = { + similarity: -1, match: null, }; if (this.foes.length < 1) return result; @@ -384,8 +385,8 @@ export default class FightScene extends Phaser.Scene { transcription, foe.beWord.ocr_transcript, ).similarity; - if (similarity < result.score) return; - result = { score: similarity, match: foe }; + if (similarity < result.similarity) return; + result = { similarity: similarity, match: foe }; }); // match ??= scene.foes[0]; // TODO: remove this // console.log(similarity, match.beWord.ocr_transcript); @@ -401,20 +402,42 @@ export default class FightScene extends Phaser.Scene { } submitTranscription(inputStatus: InputStatus) { + const similarityThreshold = 0.9; // NOTE: this ain't async to avoid any UX delay - const { score, match } = this.findMatchingFoe(inputStatus.final); + const { similarity, match } = this.findMatchingFoe(inputStatus.final); + + let score = 0; + if (match === null) { + score = 0; + } else if (similarity < similarityThreshold) { + score = -1; + } else { + const lengthScore = this.nthFibonacci( + 1 + match.beWord.ocr_transcript.length, + ); + const accuracyBonus = similarity; + const speedBonus = + 2 - + (inputStatus.ended_at_gmtm - match.beClue.began_at_gmtm) / + (match.duration * 1000); + score = Math.round(lengthScore * accuracyBonus * speedBonus); + } + backend.createShot(this.beGame.id, { clue_id: match?.beClue?.id || null, + similarity: similarity, + score: score, ...inputStatus, }); + if (match === null) { // NOOP this.sound.play("sfx_md_beep"); this.hud.showSubmitFeedback("white", inputStatus.final); - } else if (score < 0.9) { + } else if (similarity < similarityThreshold) { // TODO: visual near misses based on score this.sound.play("sfx_lo_beep"); - this.updateScore(-1); + this.updateScore(score); match.handleFailure(); this.hud.showSubmitFeedback("red", inputStatus.final); new Spear(this, this.player, undefined); @@ -424,21 +447,11 @@ export default class FightScene extends Phaser.Scene { ended_at: new Date().toISOString(), ended_at_gmtm: this.getGameTime(), }); - const lengthScore = this.nthFibonacci( - 1 + match.beWord.ocr_transcript.length, - ); - const accuracyBonus = score; - const speedBonus = - 2 - - (inputStatus.ended_at_gmtm - match.beClue.began_at_gmtm) / - (match.duration * 1000); - this.updateScore(Math.round(lengthScore * accuracyBonus * speedBonus)); - + this.updateScore(score); this.popFoe(match); match.handleSuccess(); this.hud.showSubmitFeedback("green", inputStatus.final); new Spear(this, this.player, match.critter); - // TODO: increase score } } -- GitLab