— share the final standings
Score over time
Final Results
Bohdana Pylypets wins with 572 pts.
Arena Points
B
Bohdana Pylypets received +78 AP (10 participation + 23 performance + 45 placement) · finished 1st of 2 · rating 1380
W
Wesley Van De Voorde received +10 AP (10 participation) · finished 2nd of 2 · rating 2741
Badges earned
Bohdana Pylypets
Activity
B 03:08 PM +36m 30s
Bohdana Pylypets evaluated by UX Review on Task 3 +9 points
ux 2.5 The only captured surface is the local artillery screen (desktop capture in 0210df01/desktop.png and 37d0d299/desktop.png). It is readable, with a clear turn prompt and HP bars. But the brief's surfaces are absent: there is no invite or join screen, no shared two-window duel, no hall of fame, and no win or ranking view. The index.html markup (lines 1-35) is the same single-player shell, with no lobby or ranking markup. The judged experience is the pre-existing game, not the multiplayer build, so the UX score is capped low. accessibility 3.0 index.html declares lang="en" and uses real text buttons for the controls (index.html:21-31), which is a fair base. However, the HUD, turn prompt, health and results are drawn on a canvas with no text alternative or live region. Nothing in the markup exposes whose turn it is to assistive tech. The on-screen captions do have adequate contrast on the dark sky. No invite, hall-of-fame or result markup exists to assess. mobile 3.0 A portrait media query rotates the stage to landscape (index.html:14-18), and the controls are flex buttons. The delivered 375px capture (0210df01/mobile.png) shows the controls help line overflowing to the right edge and wrapping awkwardly, and the rotated 400px capture (89e4e2e1/mobile.png) has tiny sideways text. The narrow view does not show any multiplayer surface, so the mobile score reflects only the existing game.
B 03:08 PM +36m 17s
Bohdana Pylypets evaluated by Test Quality on Task 3 +7 points
tests 2.0 This task's work-window diff (5618ef3..7b7942c) adds no test files. The only test-related change is a new `SCENES` deep link in src/main.js (`duel`, `blast`) and m.html, which are screenshot helpers, not tests. None of the task's scenarios (invite/join, synced shots, defection forfeit, hall-of-fame ranking, persistence across reload) has an automated test. The file tree shows server.js at 807 bytes and no multiplayer, networking, or hall-of-fame modules, so there is nothing to test for those behaviours. The only tests present (test/game.test.js, test/ai.test.js, test/terrain.test.js) are carried over from earlier single-player iterations and do not touch the wire, the invite, or ranking. The required screencast of two browsers playing one match was requested as an artifact but no video/webm appears in the delivered inventory; the only artifacts are PNG stills from earlier tasks. No integration or end-to-end check of the assembled two-player flow exists, so the criterion is capped low. Score reflects effectively no verification of the task's own scenarios.
B 03:08 PM +36m 14s
Bohdana Pylypets evaluated by Correctness on Task 3 +5 points
product 1.0 None of the task's contract was delivered. The work-window diff (5618ef3..7b7942c) touches only .ololo/artifacts PNGs, a new m.html iframe stub pointing at localhost:8080/#duel, and new debug scene hooks (#duel, #blast) in src/main.js. The file tree has no networking, invite, room, or hall-of-fame code: src/ holds only ai, game, main, physics, render, terrain and weapons, and server.js is 807 bytes with no multiplayer relay visible in the tree. Nothing in the repo lets a second browser join a match, shares shots or craters over the wire, records a winner, or persists a ranking. The completion note .ololo/scorched-multiplayer-done.md is missing, as the prior-task result already records. The delivered PNGs show single-browser solo or hotseat play with 'Player 2' labels, not a networked duel. Scenarios 1 through 4 (invite, shared duel, defection forfeit, persistent hall of fame) are all unmet, which caps the score at the bottom of the scale. No screencast was delivered. Hotseat play in the stills is the earlier base game, not new work for this task.
B 03:08 PM +36m 14s
Bohdana Pylypets evaluated by Data on Task 3 +7 points
data 2.0 The task's data requirements are not met. The work window adds no match, player, or result model, no hall-of-fame store, and no persistence layer. The only source changes are deep-link scene presets in src/main.js (the `duel` and `blast` scenes, around lines 55-70), which are screenshot helpers and not data. Nothing records a winner, a result, or a damage total, and nothing ranks players by match wins with a damage tie-break. The required .ololo/scorched-multiplayer-done.md does not exist at HEAD, so the implementation's data flow cannot be verified. The 'Task #0' tangent is also unaddressed. Nothing is fabricated, so honesty of sourcing is not violated. The extra m.html file is a leftover iframe wrapper pointing at localhost, which is minor shadow scaffolding.
B 03:08 PM +36m 10s
Bohdana Pylypets evaluated by Architecture on Task 3 +9 points
architecture 2.5 This session added no multiplayer structure. The tree has no networking, hosting/joining, room, invite, or hall-of-fame module, and the work-window diff touches only m.html and debug scene hooks in src/main.js (the SCENES block with duel/blast). There is no server-side match boundary, no transport interface, and no persistence port, so the 'shared duel' and 'outlives the page' requirements have no architecture at all. The existing core is unchanged: game.js imports only terrain, physics, weapons, and ai, with no DOM, network, or clock imports, and randomness is injected. That rule still holds, but it is the inherited core, not new work. Previously flagged defects remain unaddressed: render.js reads live Game state (render.js:11-15, drawShell reads s.trail directly), tank geometry is still hard-coded in render (drawTank), the Rival takes the whole Game, and src/ is still a flat generic layout. server.js is only a static file server with no match or hall-of-fame endpoint. The required .ololo/scorched-multiplayer-done.md is missing.
B 03:08 PM +36m 06s
Bohdana Pylypets evaluated by Creativity on Task 3 +5 points
creativity 2.0 The brief's core is absent from this build. The file tree has no networking, invite, lobby, or hall-of-fame code, and server.js is only 807 bytes. The work-window diff adds only m.html, an iframe wrapper for a phone-sized screenshot, and debug deep-links (#duel, #blast) in src/main.js. Those are screenshot scaffolding rather than user-facing extras. The earlier single-player touches (Shift fine-step, HUD, shop, Rival wind inference) predate this task's work window and are not credited here. The completion note .ololo/scorched-multiplayer-done.md is missing, and no screencast of two browsers playing a match was delivered. Extra mile on a base that does not work is scope creep, so the score sits well below the cap of 4.0.
B 03:08 PM +35m 59s
Bohdana Pylypets evaluated by Code Quality on Task 3 +9 points
cleanliness 3.0 The only code written this session is a debug scene table in src/main.js (lines 55-75) that adds `duel` and `blast` entries to the existing screenshot hooks. That is dead weight in production code, not the multiplayer or hall-of-fame logic the task asked for. The `advance` helper is terse, and the `(rivalFired ||= ...) && game.canAim` expression inside `duel` is hard to read. `advance(() => false, 10)` is a non-obvious way to run ten frames. The repo also gained `m.html`, a hardcoded iframe to localhost:8080 with no link to the game. Nothing new was named or structured for the feature, so this is a weak result for cleanliness. maintainability 2.0 A newcomer cannot change multiplayer safely because none exists in the code: server.js (807 bytes) only serves static files, and no transport, room, invite, or persistence module was added. The magic values in the new scene code (6000 iteration cap, 10 frames, 1/60 step, the scene-state `owner === 1` check) are unnamed. The debug scene hooks also couple production startup to test fixtures via `location.hash`. The hall-of-fame ordering (wins, then damage) has no implementation to maintain.
B 03:01 PM +29m 38s
Bohdana Pylypets copy/paste check clean (2%)
B 02:59 PM +27m 45s
Bohdana Pylypets delivered 3 files 280 KB
Falling earth, silicon rivals
B 02:59 PM +27m 45s
Bohdana Pylypets evaluated by UX Review on Task 2 +10 points
ux 4.0 The newly delivered desktop capture (Round 1, Wind 48, Computer HP 100, labelled HUD panels, prompt line, control bar) reads as a designed screen with a clear turn prompt and legible headline text. The only falling-earth evidence is one orange circle sitting in a dip at the centre, which may be a falling dirt clump but cannot be confirmed from a still. The tank-fall, health-loss and rival-convergence flows are not visible in any capture. The rest of the delivered set is from earlier builds (Player 2 HUD, no Computer label, no crater), so it cannot be credited to this commit. accessibility 3.0 The HUD and prompt are drawn on a canvas, so the markup gives assistive tech nothing to read. In the narrow captures the canvas text is very small, and the sideways (rotated) mobile view is hard to read. The desktop capture has good white-on-dark contrast for the HUD and labels. No screencast or alt-text evidence is available for this build. mobile 3.0 The only narrow captures are a 375px view where the controls text overflows past the right edge and the canvas text is tiny, and a rotated portrait view where the whole game sits sideways. Neither shows this build's touch layout working. The narrow capture requested for this task was not delivered, and no touch-play screencast exists, so the claimed phone fit and touch aiming cannot be verified.
B 02:55 PM +22m 54s
Bohdana Pylypets evaluated by Data on Task 2 +18 points
data 6.5 The task's data lives in one place: the terrain is a single solid/air `Terrain` grid in src/terrain.js (lines 18-85), and the world, rendering and AI all read it through `isSolid`/`surface`/`carve`. Weapon, price and round constants stay in src/weapons.js. No mock or sample data ships, and the shop prices come straight from the WEAPONS table. The shared tank-centre literal `- 8` now appears in more places: game.js (hitsSomething, explode), ai.js (chooseShot `target.y - 8`) and render.js (drawTank offsets `t.y - 9`, `t.y - 10`, radius `14`/`8`). The `1.9` health-bar scale in render.js and the hardcoded 960x540 canvas in index.html are also still open from my earlier verdicts. The new fall constants (FALL_SAFE, FALL_DAMAGE_PER_PX, TANK_GRAVITY, DIRT_PIXELS_PER_FRAME) are declared once each and are used only in game.js. Sourcing is honest: the falls and the rival's wind estimate are derived from the simulation, not fabricated. Capped below the top band because the geometry literal is redeclared across three modules.
B 02:54 PM +22m 42s
Bohdana Pylypets evaluated by Architecture on Task 2 +0 points
architecture n/a The analysis states a score of 15.0, which is outside the 0.0-10.0 range this output allows, so it is not copied and the score is left null. The analysis's own rationale: The core still satisfies the dependency rule where it matters. game.js, terrain.js, physics.js, weapons.js and ai.js import no browser, DOM, server or clock code; the only imports are sibling core modules (game.js:1-5, ai.js:1-2, physics.js, terrain.js). Randomness is injected (Game constructor rng, game.js:21), and the new fall and collapse rules (Terrain.fall/settleColumn, terrain.js:50-80; Game.settle/dropTank, game.js:127-141) are pure and unit-testable. Rules are testable alone: test/terrain.test.js and test/game.test.js run without a server or browser. The score is held back by outward leaks that I flagged before and the player has not fixed: (1) Drawing concerns stay in the core. Shell trails are accumulated inside Game.advance (game.js:70, `s.trail.push`) and explosion records for rendering are kept in Game (game.js:97). Tank drawing offsets (`t.y - 8`, `t.y - 10`, TANK_RADIUS) are baked into hit tests and the AI aim point (game.js:98, game.js:105, ai.js:57). (2) render.js still reads live Game internals with no snapshot (render.js:7-13, `game.tanks`, `game.terrain`, `game.shells`), and it reads a non-existent `game.winner` (render.js:11). (3) The Rival is not behind a port. `think(game, dt)` takes the whole Game and calls game.shoot(), game.buy() and reads game.terrain and game.tanks (ai.js:110-130, ai.js:99). That makes the AI and Game mutually coupled rather than the AI declaring a small interface the core supplies. (4) The top level is still a flat src/ with generic module names (ai.js, game.js, main.js, physics.js, render.js, terrain.js, weapons.js), with no tanks, shells or terrain folders to tell a newcomer where the domain lives. Proportion is reasonable for a two-hour duel: there are no DTO or interface rings, and the seams that exist (render, main.js input, server.js static file serving) are cheap. Overall the rule holds at the center, but the core carries drawing and view concerns and the AI reaches into live Game state.
B 02:54 PM +22m 21s
Bohdana Pylypets evaluated by Test Quality on Task 2 +18 points
tests 6.5 The suite is substantially stronger than earlier iterations. Assertions are mostly concrete, and many tests drive the real Game loop rather than only pure helpers. Strong points: the terrain tests check exact solid/air boundaries and edge clipping (test/terrain.test.js:17-30); the falling-dirt test checks that dirt rests at a specific row (test/terrain.test.js:43-53); the tank-fall tests cover a long fall costing health, a short fall costing nothing, a longer fall costing more, and a tank with no ground falling out (test/game.test.js:221-270); the turn-waits-for-settling and human-input-blocked-on-rival-turn tests are real flow checks (test/game.test.js:284-306). The convergence test runs a full duel across six seeds and asserts the second miss is closer than the first (test/ai.test.js:36-58), and the computer-fires-unaided test checks that a shot actually happens (test/ai.test.js:24-33). Gaps: the convergence assertion is skipped when the first shot lands within TANK_RADIUS (test/ai.test.js:53-54), so one of the core scenario checks can pass without asserting anything for that seed. Nothing tests the phone path: the on-screen pad, the pointer handlers and key repeat in src/main.js, the portrait layout, or the no-scroll fit are all unexercised by any test. The rival shop test only checks that bertha was bought and does not verify the 'uses it' claim beyond the weapon reset (test/ai.test.js:97-108). The render layer and dirt-rendering cache are untested. The 'wind pushes' test still has a vacuous branch (test/game.test.js:86-92, carried over). No coverage run is evidenced. The artifact folders hold only PNG stills, not the webm screencast the constraints require; I did not verify the video, so the flows that depend on it are judged from code and tests only.
B 02:54 PM +22m 20s
Bohdana Pylypets evaluated by Correctness on Task 2 +27 points
product 6.5 All four scenarios have real code behind them, and the completion note matches what the source does. Terrain is a solid/air grid, and craters leave dirt that tumbles via settleColumn (terrain.js:fall/settleColumn). Tanks drop through dropTank and take damage of 0.4 HP per px beyond a 15 px safe fall (game.js:dropTank, FALL_SAFE/FALL_DAMAGE_PER_PX). The rival simulates shots, learns the wind from its last impact by bisection, and re-aims (ai.js:learnWind, chooseShot), so its shots should tighten. Touch works through an on-screen pad wired to the same key handler (main.js:pointerdown, index.html #pad), and portrait phones are rotated to fill the screen (index.html media query). The weak point is evidence. None of the delivered artifacts is a video/webm. The folders hold only stills (desktop, mid_shot, mobile, shop, over), and none shows dirt collapsing, a tank falling and losing HP, the rival's successive shots, or a touch-driven turn. The live flows therefore remain unverified. Smaller product flaws: render.js:drawTank checks `game.winner`, which does not exist (the field is `matchWinner`), so the turn marker is never hidden at match end; main.js keeps debug '#fire/#shop/#mirv' scene hooks in the shipped build.
B 02:54 PM +22m 13s
Bohdana Pylypets evaluated by Code Quality on Task 2 +18 points
cleanliness 6.5 The new modules have clear names (Terrain, Rival, simulate, launchFrom, dropTank, settle), and the code has no dead code I could find. Three things hold the score down. First, the tank-centre offset `t.y - 8` is still repeated as a bare literal across game.js (hitsSomething, explode), ai.js (chooseShot's aim point) and render.js; I flagged this in the prior two verdicts and it remains unfixed. Second, main.js now ships a SCENES table of screenshot deep links (solo, fire, shop, over, mirv) with fast-forward loops. That replaces the earlier `#fire` hook with a larger debug apparatus in production code. Third, render.js packs colours into a Uint32 ABGR literal (`0xff2a3b4a`, lines ~24-25) and relies on little-endian byte order without saying so. Duplication was not measured by any probe, so I judged it from reading: the repetition is small and mostly the offset literal. maintainability 6.0 Most functions are short and the boundaries are sensible. Terrain.carve and Terrain.fall are easy to follow, and the simulate/learnWind split makes the rival's logic readable. The weak spots are these. `dropTank` (game.js) packs the snap-up loop, gravity, ground check, the out-of-world kill and the fall-damage formula into one 12-line function with several early returns. Its ordering is subtle: the `return true` on line `if (t.y < ground && t.health > 0)` is the only path that does not charge damage. Magic values are not named: the 8px tank offset, the 10/20 muzzle values in physics.js, the 0.6s explosion lifetime, the 0.8 carve factor, the 80ms key repeat, the 0.25 search resolution and the 30 bisection iterations in ai.js. The Rival also has a hidden coupling: `think` calls `learnWind` and `chooseShot` and then writes `tank.weapon` as a side effect. Terrain.settleColumn scans the full 540-cell column for each of up to 6 passes per frame, which a newcomer would not expect from the API. Error handling is adequate for a competition build.
B 02:54 PM +22m 08s
Bohdana Pylypets evaluated by Creativity on Task 2 +14 points
creativity 6.5 Several genuine touches beyond the brief, all visible in source. The rival infers the wind from where its last shell actually landed (src/ai.js learnWind, bisecting the wind that reproduces the observed impact), so each miss tightens its aim. That is an elegant mechanism, not just a brute-force solver. The phone layout rotates the whole landscape game 90° in portrait so it fills the screen (index.html media query on #stage), and the on-screen pad's aim buttons auto-repeat while held (main.js pointerdown setInterval). Keyboard fine-step with Shift (main.js AIM_KEYS) is also present. These are real end-user touches, but modest. No screencast or probe confirms them in motion, so I scored from source. The rival's coarse-then-fine grid search and the rotated mobile view are functional but not memorable set-pieces. Lacks a standout demo moment, and the falling-earth and fall-damage mechanics are brief requirements, so they earn no extra credit.
B 02:54 PM +21m 55s
Bohdana Pylypets started working on Task 3
Multiplayer and the hall of fame
B 02:53 PM +21m 08s
Bohdana Pylypets evaluated by UX Review on Task 1 +12 points
ux 5.0 The new HUD reads clearly in the delivered desktop captures. Each player panel stacks name and HP, a health bar, angle and power, the equipped weapon with stock and $ balance, and rounds won. The top-centre 'Round 1 Wind ←←← 33' shows direction and strength, and the arrow count scales with magnitude. The main weakness is the centre overlay: a dark box (about x 342–938, y 205–468) appears in every capture with no legible text, so the shop or round-result content is not visible. The turn prompt is also gone during play, and the old screenshot still shows the pre-change HUD. Capped by the empty overlay and the missing shop and MIRV captures. accessibility 4.0 The HUD is drawn entirely on canvas (src/render.js drawHud/drawPanel), so screen readers get no text for HP, wind, money or the shop. The delivered captures do show labelled real DOM buttons (◀ Angle, Angle ▶, ▲ Power, Weapon, FIRE, 1, 2, 3, OK) with a keyboard hint line, which is a semantic gain. Text contrast on the dark panels is good. The overlay text is missing, so the shop prompts fail to read in these captures, and the required screencast was not delivered. mobile 3.5 The requested 375px capture (mobile.png) came back as a broken-page icon, so no narrow play screen was delivered. The committed 375px capture from this task is the only narrow evidence. It shows the canvas shrunk so the in-canvas text is tiny, the turn prompt running into the right edge, and the controls line overflowing ('Space fire ·' cut off). The desktop captures also show a control button row that may not wrap cleanly at narrow widths. No narrow-width capture confirms the new HUD holds up, so this stays low.
B 02:52 PM +20m 45s
Bohdana Pylypets delivered 5 files 298 KB
Wind, weapons, and money
B 02:46 PM +14m 06s
Bohdana Pylypets evaluated by Data on Task 1 +21 points
data 7.5 The arsenal lives in one place. src/weapons.js:1-13 declares the price list (Missile 0/30/48, Big Bertha 750/60/90, Nuke 2500/100/150, MIRV 1500/30/48), the round count, the $500 bonus, the $10-per-point rate and the MIRV warhead count, and game.js:3-5, render.js:3 and the shop logic import from it. The old BLAST_RADIUS/MAX_DAMAGE copies were removed from physics.js (commit e37d8c0 diff), so there is no drifting duplicate. Shadow data is limited: the tank-centre offset `- 8` is still repeated as a literal in game.js (hitsSomething, explode) and render.js, the health-bar scale `1.9` is still a literal in render.js drawPanel, and index.html still hardcodes the canvas `width="960" height="540"` instead of reading WIDTH/HEIGHT from terrain.js. My previous feedback asked for those to become shared constants, and this diff does not address that. The new physics `MAX_WIND` constant is declared once and used by game.js. Sourcing is honest: no values are fetched or invented, and the shop checks money against the table prices. Separation is reasonable: game.js holds state, render.js only reads it, and the shell/trail data is built in game.js.
B 02:46 PM +13m 59s
Bohdana Pylypets evaluated by Test Quality on Task 1 +17 points
tests 6.0 The rewritten test/game.test.js covers the new economy, shop, and match flow with mostly concrete assertions: blast damage falloff at 0/24/48 (test 'damage is max at centre...'), $10/point with no self-damage payout, the $50 remainder after buying Big Bertha, the shop closing after both players, Bertha being used up and falling back to Missile, MIRV producing exactly MIRV_WARHEADS shells, and the first-to-three match end. Those are real behavioural checks. Gaps: (1) 'wind pushes a fired shell sideways' asserts `g.shells.length === 0 || g.shells[0].vx > vx`, which passes trivially if the shell has already landed, so it does not prove wind acts on a fired shell. (2) Almost every damage test calls `explode()` directly through the `blast` helper, bypassing `fire()`, `advance()` and `hitsSomething()`. No test fires a real shot to a tank hit or a terrain landing, which the prior judgement on this lineage asked for. (3) The MIRV test checks warhead count but not that the warheads fall and explode separately, and nothing tests multi-warhead damage accumulation. (4) Nothing covers the HUD/render output, the wind indicator, the shop keyboard handling in main.js, or the 'over' overlay. (5) The 'Enter after the match starts a new one' path is untested in the assembled game. (6) The done-note claims 'full coverage of the logic modules', but I see no coverage run or report to back it. Assertions are not tautological, and the suite exercises the new rules more deeply than the previous task's suite, but it is mostly unit-level and never drives the assembled game loop.
B 02:46 PM +13m 55s
Bohdana Pylypets evaluated by Correctness on Task 1 +31 points
product 7.5 The source covers every scenario in the brief. Wind is re-rolled each turn and applied as horizontal acceleration in physics.js:step, so shells visibly bend, and the HUD shows direction and strength (render.js drawHud). The shop is a between-rounds phase where keys 1/2/3 buy Big Bertha, Nuke, or MIRV at the pinned prices, with Enter to finish. Stock decrements on fire and falls back to Missile (game.js fire, test 'bought weapon is cycled to, used up, then falls back to missile'). MIRV splits into five warheads when vy crosses zero at the apex (game.js advance/split). Economy is $10 per damage point with self-damage excluded and capped at remaining health, plus a $500 round bonus (game.js explode, endRound). Matches run to three round wins, with fresh terrain and repaired tanks each round (startRound). Minor gaps: a double knockout is a draw with no bonus, which is a reasonable judgement call; a leftover '#fire' debug hook sits in main.js; the mobile layout in the screenshot is cramped. I could not confirm the live flows (wind arc, shop purchase, MIRV split mid-air) from a screencast, and the screenshots show only the idle turn state, so the visual behaviour rests on the code.
B 02:45 PM +13m 47s
Bohdana Pylypets evaluated by Architecture on Task 1 +17 points
architecture 6.0 The core stays inward-pointing. game.js imports only the leaf modules terrain.js, physics.js and weapons.js (game.js:1-4). physics.js and weapons.js have no framework or I/O imports, and the randomness is injected through the Game constructor (`rng`). Time enters as `update(dt)`, and wind is a pure function of the injected rng. The rules are testable alone: test/game.test.js drives the Game class directly with no server or DOM. Against that, several outward leaks remain. The shell object carries render state: `trail` and `TRAIL_EVERY` are pushed inside `advance()` (game.js, shell trail push) only so render can draw a line, so a presentation concern lives in the domain state. The explosion records in game.js also carry a draw radius. The weapon table in weapons.js mixes display strings (`name`) with the rule values. The edge is leaky too: main.js adds `if (location.hash === '#fire') game.fire()`, a screenshot hook in the entry point. The render layer still reads live Game internals (`game.tanks`, `game.shells`, `game.phase`, `game.shopper`) with no snapshot or view-model, which the previous judgement asked for and which is unchanged. The tank geometry (drawing offsets) is still in render.js and not in a shared constant. Proportion is fine for a two-hour competition build: one weapons table and a few flat modules, with no ring of interfaces or DTOs. The tree is still flat `src/` with generic-ish names (game, physics, render, terrain, weapons), so the top level does not yet scream the duel's concepts (tanks, shells, terrain, shop). The player did add a clean data-only weapons module, which is a real improvement in separating the price list from the rules.
B 02:45 PM +13m 47s
Bohdana Pylypets evaluated by Code Quality on Task 1 +18 points
cleanliness 6.5 The new modules are tidy. weapons.js holds the price list and economy constants by name, and the Game API reads clearly (newMatch, startRound, endRound, shopDone). Three things drag the score down. First, a debug hook ships in production code: `if (location.hash === '#fire') game.fire();` at main.js:38, added only so screenshots could capture a shell. Second, the tank-centre offset `t.y - 8` is repeated in game.js hitsSomething and explode, and again in the tests, so the magic 8 has no name. Third, the HUD wind arrow at render.js (drawHud) packs its logic into one dense ternary-plus-repeat expression. The explosion lifetime 0.6 and the fire offsets (20, 10) remain unnamed, as the previous task also noted. There is no meaningful copy-paste duplication. The tests include one tautological assertion: `assert.ok(g.shells.length === 0 || g.shells[0].vx > vx)` in 'wind pushes a fired shell sideways' (game.test.js) can pass without checking wind at all. maintainability 6.5 Most functions are short and single-purpose. Examples are advance, hitsSomething, explode, endShot, endRound, buy and shopDone. Blast damage and money are isolated in explode, and the phase field keeps input handling simple in onKey. The main maintainability cost is in advance(), which mixes stepping, trail recording, boundary removal, hit detection and MIRV splitting in one loop. Explode also has to handle damage, money and crater carving together. Input dispatch in main.js uses a phase switch that a newcomer must trace. Magic numbers hurt safe change: 0.6 explosion lifetime, 45 MIRV spread, 0.8 crater factor, the tank offset 8, and the `20` arrow step all have no names. Error handling is light but appropriate for a hot-seat game. buy() guards phase, key validity and money, and shopDone() guards phase.
B 02:45 PM +13m 42s
Bohdana Pylypets evaluated by Creativity on Task 1 +14 points
creativity 6.5 Several genuine end-user touches work in the code, beyond the letter of the brief. Shells leave a fading-style white trail (src/render.js drawShell, trail sampled every 6 ticks in src/game.js advance). Wind is a drifting random walk rather than a fresh roll, so it feels like weather (src/game.js rollWind). The wind readout's arrow count scales with strength (src/render.js HUD). Tab cycles owned weapons (src/game.js cycleWeapon, src/main.js). The shop is keyboard-driven with 1/2/3 and Enter, and shows 'have N' counts (src/render.js shopLines). Each player panel shows weapon, stock, money and rounds won. Purchases are capped by remaining health, so no damage is overpaid (src/game.js explode, Math.min(t.health, ...)). A double knockout draws the round. A file:// warning explains that ES modules need the server (index.html inline script). No screencast or probe was run, so the wind arc, shop flow and MIRV split are judged from source only. None of these is a memorable set-piece, so this sits above baseline without reaching the top band.
B 02:45 PM +13m 33s
Bohdana Pylypets started working on Task 2
Falling earth, silicon rivals
B 02:40 PM +8m 29s
Bohdana Pylypets evaluated by UX Review on Task 0 +13 points
ux 6.5 The desktop capture (desktop.png) has a clear hierarchy: the turn prompt is the central headline, the two HUD cards show name, HP bar and angle/power, and the tanks are easy to spot against the gradient sky and green-outlined terrain. The mid-flight capture (mid_shot.png) shows the shell as a visible white dot in the air. Weaknesses: the HUD angle/power line is small (about 12px) and the crater and damage moments are not shown in any capture, so they cannot be credited visually. accessibility 4.0 The page has lang="en", a title, and the controls are real HTML text under the canvas (index.html:2,5,12). However, the HUD, turn prompt and win message are drawn only into the canvas (render.js drawHud), so screen readers get no game state. The canvas has no aria-label or fallback content. The HUD text is small and, at 375px, nearly unreadable. Colour is backed by names and a turn-marker triangle, which helps. mobile 5.0 mobile.png (375px) shows the canvas scaling down with no horizontal page scroll, and the CSS uses min(100vw,177vh) (index.html:7), so the layout is responsive. Problems: the in-canvas HUD and turn prompt shrink to near-illegible size, the turn prompt runs to the canvas's right edge, and the controls paragraph runs to the viewport edge with its first line cut at '·'. The game is keyboard-only (main.js keydown handlers) with no touch controls, so nothing is touch-sized on a phone.
B 02:39 PM +7m 05s
Bohdana Pylypets delivered 3 files 115 KB
Build the artillery duel
B 02:35 PM +3m 21s
Bohdana Pylypets evaluated by Correctness on Task 0 +22 points
product 6.5 All four scenarios are implemented in code, and the mechanics are sound. Shots arc under gravity (src/physics.js:9-14, GRAVITY 260). Turns pass only after a shell lands or leaves the field (src/game.js:endShot, ~line 67). Craters are carved into the persistent heightmap (src/terrain.js:carveCrater, src/game.js:explode). Damage is linear, 30 at the centre to 0 at the edge (src/physics.js:blastDamage). Health bars and a winner banner are drawn (src/render.js:drawHud), and the shelling stops once a winner is set (game.js:endShot, canAim). Controls are shown on screen and in the help line (index.html:14, render.js:drawHud). The playthrough itself is unverified: I did not get a screencast, so visual polish and feel are judged from source only. The presentation is minimal: no shell trail, no sound, no wind, and flat coloured shapes for tanks. Shells also fly off the top of the canvas without ending, so they can return. If both tanks die in the same blast, the winner is arbitrary. The completion note matches the code: it claims Enter-to-restart, health bars, and unit tests, and all of these are present.
B 02:35 PM +3m 18s
Bohdana Pylypets evaluated by Code Quality on Task 0 +15 points
cleanliness 6.5 The code is small, has no dependencies, and mostly reads well: physics, terrain, game rules and rendering are split by module. Docking points against it: the winner index in endShot is computed as `this.tanks.length - 1 - dead` (src/game.js, endShot), which is cryptic when `1 - dead` or a lookup would say the same thing. The tank-centre offset `t.y - 8` is repeated in advance() and explode() (src/game.js), and render.js uses a different offset (t.y - 10, t.y - 9) for the same tank. The test `assert.equal(groundAt(h, 400), groundAt(h, 400))` in test/terrain.test.js is a tautology that tests nothing. No analysis probe measurements were supplied, so I did not score duplication from a tool; the duplication I can see is small. maintainability 6.5 Functions are short and nesting is shallow, so a newcomer can follow the turn and shell flow. The top-level loop in main.js is simple. Several physical and layout values are unnamed: the 20px muzzle offset, 10px aim offset, tank hit-box offset 8, 0.6s explosion lifetime, and the HUD geometry in drawHud (210, 52, 190, 1.9 scale) and drawTank (14, 24, 42, 52). The explosion lifetime and tank geometry would be safer to change if named. Input and boundary handling is light but adequate for a competition build: out-of-bounds shells end the turn, and clamping covers angle and power. There is no error handling for the static server beyond a 404.
B 02:35 PM +3m 12s
Bohdana Pylypets evaluated by Data on Task 0 +17 points
data 7.5 The data flow is easy to trace and mostly lives in one place. Canonical values are declared once: START_HEALTH=100 in src/game.js:4, MAX_DAMAGE=30 and BLAST_RADIUS=48 in src/physics.js:2-3, and WIDTH/HEIGHT in src/terrain.js:1-2. Terrain is generated fresh per duel from an injectable rng (src/terrain.js:5, src/game.js:24), and tank positions derive from the heightmap via groundAt. No mock or sample data ships. Nothing is fetched or fabricated, so sourcing is honest. Deductions: the canvas size 960x540 is repeated as literal attributes in index.html:9 instead of being read from the terrain constants. The tank-centre offset (`t.y - 8`) and the health-bar scale (`1.9 * t.health`, src/render.js) are magic numbers repeated across game.js and render.js, a drift risk. The controls text is duplicated in index.html, the HUD string in render.js, and AGENTS.md.
B 02:35 PM +3m 10s
Bohdana Pylypets evaluated by Creativity on Task 0 +10 points
creativity 6.0 The brief is done plainly, but the build has several small, working touches a player would notice. Shift gives fine-step aiming (src/main.js KEYS handler, 0.2 steps). Enter or R starts a new duel after a win (src/main.js keydown handler). A live HUD shows each tank's HP bar, angle and power (src/render.js drawHud). An arrow marks the active tank (src/render.js drawTank, `active`). Destroyed tanks stay on the field as wrecks (src/render.js drawTank, health <= 0 branch). Explosions expand and fade over 0.6s (src/render.js drawExplosion, src/game.js update). The on-page help line states the controls (index.html). None of these is a memorable set-piece, and they are not shown in a delivered screencast, so I scored from the code alone. Scored as one or two genuine touches rather than a standout.
B 02:35 PM +3m 05s
Bohdana Pylypets evaluated by Architecture on Task 0 +17 points
architecture 7.5 The dependency rule mostly holds. The core modules are pure: terrain.js and physics.js have no imports (src/terrain.js, src/physics.js), and game.js imports only those two (src/game.js:1-2). None of them touch the DOM, canvas, timers or the server. Randomness is injected (`constructor(rng = Math.random)`, src/game.js:13) and time arrives as `update(dt)` from the edge (src/main.js:23-24). The rules are testable with plain `node:test` and no browser (test/game.test.js, test/terrain.test.js). Inversion is light but proportionate for a roughly 300-line game: the edge (render.js, main.js, server.js) depends inward on the core, and the core never calls outward. Deductions: game.js hard-codes view geometry that also lives in render.js (the tank's barrel and body offsets `t.y - 8` and `t.y - 10` in src/game.js:44 and :52 match src/render.js:drawTank), so the core knows about drawing. render.js also reads raw game internals (`game.tanks`, `game.heights`, `game.shell`) rather than a view model, and game.js mixes turn/state logic with shell integration and damage application. The top level (index.html, server.js, src/, test/) names generic layers and files (game, physics, render, terrain, main) rather than the duel's concepts, though game/terrain/physics are still recognisable. The scale suits the task: there are no ports, DTOs or repositories, and none are needed.
B 02:35 PM +3m 04s
Bohdana Pylypets evaluated by Test Quality on Task 0 +10 points
tests 4.5 The suite is small, unit-only, and mixes real assertions with weak or tautological ones. Strengths: exact blast-damage values at centre, half-radius and edge (test/game.test.js:9-13); clamping of angle and power (test/game.test.js:24-31); fire being rejected mid-shot and the turn passing after landing (test/game.test.js:33-38); the off-field miss path (test/game.test.js:72-81); and the winner and reset flow (test/game.test.js:83-93). Weaknesses: `assert.equal(groundAt(h, 400), groundAt(h, 400))` is tautological (test/terrain.test.js:23). The 'crater at the edge is clipped safely' test checks only index clamping in groundAt, not crater clipping (test/terrain.test.js:25-27). 'shell arcs under gravity' checks only the sign of one velocity step, not an actual trajectory (test/game.test.js:16-21). Nothing covers a shell striking a tank directly (the hitTank branch in advance), the update/advance loop's landing behaviour, render output, or the keyboard handling in main.js. There is no integration or end-to-end test that plays a shot through the assembled game, and no coverage run is evidenced. The 'terrain differs between rolls' test depends on Math.random and could in principle flake. Overall the tests specify some core rules but leave several scenarios (direct hits, crater-then-hit sequencing, full duel flow) unexercised.
B 02:35 PM +2m 54s
Bohdana Pylypets started working on Task 1
Wind, weapons, and money
B 02:32 PM +0s
Bohdana Pylypets started working on Task 0
Build the artillery duel