Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
138 changes: 138 additions & 0 deletions games/Coin-Collector+.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
const player = "p"
const wall = "w"
const coin = "c"

setLegend(
[player, bitmap`
................
................
................
......0000......
.....022220.....
....02222220....
....02222220....
....02222220....
....02222220....
....02222220....
.....022220.....
......0000......
................
................
................
................`],

[wall, bitmap`
1111111111111111
1111111111111111
1111111111111111
1111111111111111
1111111111111111
1111111111111111
1111111111111111
1111111111111111
1111111111111111
1111111111111111
1111111111111111
1111111111111111
1111111111111111
1111111111111111
1111111111111111
1111111111111111`],

[coin, bitmap`
................
................
................
......3333......
.....344443.....
....34444443....
....34444443....
....34444443....
....34444443....
....34444443....
.....344443.....
......3333......
................
................
................
................`]
)

setSolids([wall])

// --- LEVELS ---
let level = 0

const levels = [
// Level 1
map`
wwwwwwwww
wp....c.w
w..ww...w
w..w....w
w..w.c..w
w....w..w
wc...w..w
wwwwwwwww`,

// Level 2
map`
wwwwwwwwwww
wp.......cw
w.w.www.w.w
w.w...w.w.w
w.w.c.w.w.w
w...w.....w
wc....c...w
wwwwwwwwwww`,

// Level 3 (Hard mode)
map`
wwwwwwwwwww
w.c...c...w
w.www.www.w
wp..c.....w
www.w.www.w
w...c..c..w
w.www.www.w
w....c....w
wwwwwwwwwww`
]

setMap(levels[level])

// --- MOVEMENT ---
onInput("w", () => getFirst(player).y--)
onInput("s", () => getFirst(player).y++)
onInput("a", () => getFirst(player).x--)
onInput("d", () => getFirst(player).x++)

// --- GAME LOGIC ---
afterInput(() => {
const p = getFirst(player)

// Remove coins if player steps on them
getTile(p.x, p.y)
.filter(t => t.type === coin)
.forEach(c => c.remove())

// Next level if all coins are gone
if (tilesWith(coin).length === 0) {
clearText()
addText("Level Complete!", { y: 1, color: color`4` })

level++

if (level < levels.length) {
setTimeout(() => {
clearText()
setMap(levels[level])
}, 500)
} else {
setTimeout(() => {
clearText()
addText("YOU WIN!", { y: 1, color: color`3` })
}, 500)
}
}
Comment on lines +127 to +137
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: A race condition allows level to be incremented multiple times due to re-entrant afterInput() calls during the 500ms delay after coin collection.
Severity: CRITICAL | Confidence: High

🔍 Detailed Analysis

The afterInput() callback increments the level variable and then schedules setMap() with a 500ms delay. During this 500ms window, if a player input occurs, the afterInput() callback re-executes. Since tilesWith(coin).length is still 0, the level variable is incremented again, and another setTimeout is scheduled. This can cause the game to skip multiple levels, corrupt game state, and trigger the "YOU WIN!" condition prematurely.

💡 Suggested Fix

To prevent re-entry, either load the new map immediately after incrementing level without a setTimeout delay, or introduce a flag to prevent afterInput() from processing level transitions if one is already in progress.

🤖 Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.

Location: games/Coin-Collector+.js#L120-L137

Potential issue: The `afterInput()` callback increments the `level` variable and then
schedules `setMap()` with a 500ms delay. During this 500ms window, if a player input
occurs, the `afterInput()` callback re-executes. Since `tilesWith(coin).length` is still
`0`, the `level` variable is incremented again, and another `setTimeout` is scheduled.
This can cause the game to skip multiple levels, corrupt game state, and trigger the
"YOU WIN!" condition prematurely.

Did we get this right? 👍 / 👎 to inform future reviews.
Reference ID: 5920927

})