Oware
4 moves ahead
mancala-engine.ts — evaluate(), minimax()
On its hardest setting this is a minimax search with alpha-beta pruning, four plies deep. It plays out every legal move, then your best replies to each, and so on, and scores the positions at the bottom.
That scoring function is where the opinions live. It reads:
10 x (its captured seeds - yours)
2 x (its seeds on the board - yours)
1 for each of your pits holding one or two seeds
Someone decided a captured seed is worth five times a seed still in play, and that your thin pits are worth steering towards because they are the ones that become capturable. Those weights are an argument about how Oware works, written as numbers. Change them and you change the machine's idea of good play.
Alpha-beta pruning means it stops examining a line once it knows that line cannot beat one it has already found. Turn on 'show the search' and watch the position count: it examines far fewer than the full tree.
On medium it does not search at all — it takes the largest immediate capture. On easy it picks a legal move uniformly at random.
Seega
3 moves ahead
seega-engine.ts — minimaxS()
Two different opponents, one per phase.
While placing, it scores each empty point: +20 per capture the placement would make, -15 if you could capture it in reply. It also runs a short search here, but during placing there is nothing to move, so that search explores an empty tree and contributes almost nothing.
Once the board is full and pieces move, the search becomes real: three plies with alpha-beta. Watch the position count jump between the two phases.
Draughts
2 moves ahead
draughts-engine.ts — evaluateD(), minimaxD()
On hard, ten points per captured piece plus a two-ply alpha-beta search. On medium there is no search at all — it scores a move as the change in material it produces, which makes it greedy and easy to bait.
Worth noting against the tournament rules: international draughts requires you to take the longest available capture. This engine does not enforce that, so both you and it can play captures a referee would disallow.
Morabaraba
2 moves ahead
morabaraba-engine.ts — evaluate(), minimax()
Three rules run before any search: if a placement completes its own mill it plays it; if one blocks a mill of yours it plays that; only then does it search.
The search is minimax with alpha-beta at two plies. Its scoring counts mills (10 each), two-in-a-rows (2 each), and material (3 per cow), from its side minus yours.
Because the board now carries the four corner diagonals, each corner sits on three mills instead of two. Nothing in the AI was changed to account for that — it simply counts mills, so corners became more valuable to it automatically. That is a case where fixing the board improved the opponent without touching the opponent.
Fanorona
1 move ahead
fanorona-engine.ts — minimaxF()
The prototype's documentation claims a two-ply search. The code passes a depth of one, so it sees your immediate reply and no further.
It adds five points per piece captured to whatever the search returns. On medium it does not search: it counts the capture and subtracts half a point for every reply you would have, a crude way of preferring moves that leave you with fewer options.
Fanorona's compulsory-capture and chaining rules mean the branching factor swings wildly from turn to turn. Watch the candidate count: some turns it has one legal move, others a dozen.
— Scores each move once, no lookahead
Bao
1 move ahead
bao-engine.ts — aiTurn()
This is not a search. It sows from each of its pits in turn, scores the result at five points per seed captured, and takes the best.
On hard it adds one look at your reply — but only at the first five of your legal moves. The code says so plainly: `// limit for performance`. If your best answer happens to be the sixth in the list, it never sees it.
That truncation is worth dwelling on. It is not an approximation with a principle behind it, like alpha-beta pruning, which only discards lines it has proved cannot matter. It is an arbitrary cut made to keep the frame rate up, and it means the opponent has a blind spot with no theory behind where the blind spot falls.
Whot
whot-engine.ts — aiChooseCard()
No search and no lookahead. It scores each playable card in its hand against a list someone wrote by hand: +40 for a draw-3, +30 for a draw-2, +25 for a skip, +20 for a go-again, +5 for matching the current shape.
Does it cheat? No. It reads how many cards you are holding — which you can see too at a real table — and if you are down to three or fewer it adds 30 to any card that would skip you or make you draw. It never reads which cards you hold.
On medium it takes its best-scoring card only 60% of the time and otherwise plays at random, which is how this game manufactures a middle difficulty: not by thinking less well, but by sometimes not thinking at all.
— No opponent — a timer or a spawner
Nothing is playing against you. There is no opponent in this game at all — no model of you, no plan, no choices being weighed. What feels like an adversary is a spawn timer and a random number generator: obstacles appear on a schedule that tightens as your score rises.
This is worth noticing precisely because it can feel otherwise. Difficulty that rises with time reads as an opponent responding to you, and it is not.
Nothing is playing against you. There is no opponent in this game at all — no model of you, no plan, no choices being weighed. What feels like an adversary is a spawn timer and a random number generator: obstacles appear on a schedule that tightens as your score rises.
This is worth noticing precisely because it can feel otherwise. Difficulty that rises with time reads as an opponent responding to you, and it is not.
Nothing is playing against you. There is no opponent in this game at all — no model of you, no plan, no choices being weighed. What feels like an adversary is a spawn timer and a random number generator: obstacles appear on a schedule that tightens as your score rises.
This is worth noticing precisely because it can feel otherwise. Difficulty that rises with time reads as an opponent responding to you, and it is not.
Nothing is playing against you. There is no opponent in this game at all — no model of you, no plan, no choices being weighed. What feels like an adversary is a spawn timer and a random number generator: obstacles appear on a schedule that tightens as your score rises.
This is worth noticing precisely because it can feel otherwise. Difficulty that rises with time reads as an opponent responding to you, and it is not.
Nothing is playing against you. There is no opponent in this game at all — no model of you, no plan, no choices being weighed. What feels like an adversary is a spawn timer and a random number generator: obstacles appear on a schedule that tightens as your score rises.
This is worth noticing precisely because it can feel otherwise. Difficulty that rises with time reads as an opponent responding to you, and it is not.
Nothing is playing against you. There is no opponent in this game at all — no model of you, no plan, no choices being weighed. What feels like an adversary is a spawn timer and a random number generator: obstacles appear on a schedule that tightens as your score rises.
This is worth noticing precisely because it can feel otherwise. Difficulty that rises with time reads as an opponent responding to you, and it is not.
Nothing is playing against you. There is no opponent in this game at all — no model of you, no plan, no choices being weighed. What feels like an adversary is a spawn timer and a random number generator: obstacles appear on a schedule that tightens as your score rises.
This is worth noticing precisely because it can feel otherwise. Difficulty that rises with time reads as an opponent responding to you, and it is not.
Nothing is playing against you. There is no opponent in this game at all — no model of you, no plan, no choices being weighed. What feels like an adversary is a spawn timer and a random number generator: obstacles appear on a schedule that tightens as your score rises.
This is worth noticing precisely because it can feel otherwise. Difficulty that rises with time reads as an opponent responding to you, and it is not.
Nothing is playing against you: your opponent is the clock. The cards are shuffled once at the start and nothing is decided after that.
Nothing is playing against you. There is no opponent in this game at all — no model of you, no plan, no choices being weighed. What feels like an adversary is a spawn timer and a random number generator: obstacles appear on a schedule that tightens as your score rises.
This is worth noticing precisely because it can feel otherwise. Difficulty that rises with time reads as an opponent responding to you, and it is not.
Nothing is playing against you. There is no opponent in this game at all — no model of you, no plan, no choices being weighed. What feels like an adversary is a spawn timer and a random number generator: obstacles appear on a schedule that tightens as your score rises.
This is worth noticing precisely because it can feel otherwise. Difficulty that rises with time reads as an opponent responding to you, and it is not.
Nothing is playing against you. There is no opponent in this game at all — no model of you, no plan, no choices being weighed. What feels like an adversary is a spawn timer and a random number generator: obstacles appear on a schedule that tightens as your score rises.
This is worth noticing precisely because it can feel otherwise. Difficulty that rises with time reads as an opponent responding to you, and it is not.
Nothing is playing against you. There is no opponent in this game at all — no model of you, no plan, no choices being weighed. What feels like an adversary is a spawn timer and a random number generator: obstacles appear on a schedule that tightens as your score rises.
This is worth noticing precisely because it can feel otherwise. Difficulty that rises with time reads as an opponent responding to you, and it is not.
Nothing is playing against you. There is no opponent in this game at all — no model of you, no plan, no choices being weighed. What feels like an adversary is a spawn timer and a random number generator: obstacles appear on a schedule that tightens as your score rises.
This is worth noticing precisely because it can feel otherwise. Difficulty that rises with time reads as an opponent responding to you, and it is not.
Nothing is playing against you. There is no opponent in this game at all — no model of you, no plan, no choices being weighed. What feels like an adversary is a spawn timer and a random number generator: obstacles appear on a schedule that tightens as your score rises.
This is worth noticing precisely because it can feel otherwise. Difficulty that rises with time reads as an opponent responding to you, and it is not.
Nothing is playing against you. There is no opponent in this game at all — no model of you, no plan, no choices being weighed. What feels like an adversary is a spawn timer and a random number generator: obstacles appear on a schedule that tightens as your score rises.
This is worth noticing precisely because it can feel otherwise. Difficulty that rises with time reads as an opponent responding to you, and it is not.