# Known equivalent mutants for scripts/mutate.mjs: mutants that survive because they cannot change behaviour on any
# state the program can reach, not because a test is missing. One per line:
#   <file>: <original line>  =>  <mutated line>  # <why it is equivalent>
# Copy the "allow-list line" the script prints for a survivor and replace <reason>. Only add a line after you have
# convinced yourself the mutant is equivalent; otherwise it is a test gap, and the fix is a test.
pong/rules.js: (nx === PLAYER_X && vel.dx < 0 && hits(player, ny)) ||  =>  (nx === PLAYER_X && vel.dx <= 0 && hits(player, ny)) ||  # vel.dx is always -1 or 1 (initialPong sets 1, a bounce negates it, serve sets -1 or 1), so < 0 and <= 0 agree
pong/rules.js: (nx === PLAYER_X && vel.dx < 0 && hits(player, ny)) ||  =>  (nx === PLAYER_X && vel.dx < 1 && hits(player, ny)) ||  # vel.dx is always -1 or 1, so < 0 and < 1 agree
pong/rules.js: (nx === CPU_X && vel.dx > 0 && hits(cpu, ny))  =>  (nx === CPU_X && vel.dx >= 0 && hits(cpu, ny))  # vel.dx is always -1 or 1, so > 0 and >= 0 agree
pong/view.js: text(320, 200, state.score.player > state.score.cpu ? 'You Win!' : 'CPU Wins', 32),  =>  text(320, 200, state.score.player >= state.score.cpu ? 'You Win!' : 'CPU Wins', 32),  # only shown when status is Over; a match ends the moment one side reaches WIN and only one point is scored per frame, so the scores are never equal there
check.js: .map((s, i) => (i > 0 && !sameHead(s.snake, states[i - 1].snake) ? i : -1))  =>  .map((s, i) => (i > 0 && !sameHead(s.snake, states[i - 1].snake) ? i : -2))  # -1 is only a "no move here" marker that the next line filters out with i >= 0; -2 is filtered out the same way
check.js: .filter((i) => i >= 0)  =>  .filter((i) => i > 0)  # the indices kept come from i > 0 on the line above, so 0 never occurs and >= 0 and > 0 agree
check.js: .filter((i) => i >= 0)  =>  .filter((i) => i >= 1)  # the indices kept come from i > 0 on the line above (integers >= 1, or the marker -1), so >= 0 and >= 1 agree
safeio.js: return isDone(r.state) ? [r.state, toArray(outs)] : go(r.state, budget * 2, outs)  =>  return isDone(r.state) ? [r.state, toArray(outs)] : go(r.state, budget * 3, outs)  # the budget only decides how many steps run per chunk; the chunks' outputs are joined in order, so any growth factor >= 2 gives the same result (it only changes the stack depth bound)
safeio.js: return go(start, 1, leaf([]))  =>  return go(start, 2, leaf([]))  # the first budget only decides the chunk sizes; the outputs are joined in order and runFor stops at isDone, so the result is the same
life/rules.js: const neighbours = (p) => offsets.map(([dx, dy]) => ({ x: p.x + dx, y: p.y + dy })).filter(onBoard)  =>  const neighbours = (p) => offsets.map(([dx, dy]) => ({ x: p.x - dx, y: p.y + dy })).filter(onBoard)  # the eight offsets are symmetric (with (dx, dy) the list also holds (-dx, dy)), so negating dx gives the same set of neighbours, only in another order, and only their count is used
life/rules.js: const neighbours = (p) => offsets.map(([dx, dy]) => ({ x: p.x + dx, y: p.y + dy })).filter(onBoard)  =>  const neighbours = (p) => offsets.map(([dx, dy]) => ({ x: p.x + dx, y: p.y - dy })).filter(onBoard)  # the eight offsets are symmetric (with (dx, dy) the list also holds (dx, -dy)), so negating dy gives the same set of neighbours, only in another order, and only their count is used
life/rules.js: : [...cells.filter((c) => index(c) < index(p)), { x: p.x, y: p.y }, ...cells.filter((c) => index(c) > index(p))]  =>  : [...cells.filter((c) => index(c) <= index(p)), { x: p.x, y: p.y }, ...cells.filter((c) => index(c) > index(p))]  # this branch is only reached when no cell has p's index (the line above removes p if it is present), so < and <= agree
life/rules.js: : [...cells.filter((c) => index(c) < index(p)), { x: p.x, y: p.y }, ...cells.filter((c) => index(c) > index(p))]  =>  : [...cells.filter((c) => index(c) < index(p)), { x: p.x, y: p.y }, ...cells.filter((c) => index(c) >= index(p))]  # this branch is only reached when no cell has p's index (the line above removes p if it is present), so > and >= agree
