Meet your new opponents: smarter, livelier bots
Bots now have personalities — they trade, build, bid, target the leader, and even react with stickers. Every game feels different.

In plain words
Playing against the computer used to feel a little flat: the old bots rolled, bought what they landed on, and not much else. Not anymore. Your opponents now have real personalities, and they actually play to win.
Every bot is mixed from a difficulty (easy, medium or hard) and a character. You might face a cautious banker who hoards cash, a reckless gambler who loves the Jackpot, a specialist who chases one colour group, or a vindictive rival who ganges up on whoever is in the lead — you.
- They send you trade offers — a spare property plus cash for the field they need — and they answer yours.
- They build by value, putting houses where the rent pays off fastest.
- They bid in auctions, and a hard bot will overpay to complete a set.
- They react in chat with stickers when a deal stings or a big rent lands.
Because difficulties and characters are shuffled into each table, no two games play out the same way. The board has a personality now.
For the technically curious
Each bot carries an optional profile — a difficulty tier crossed with one of five archetypes (monopolist, specialist, vindictive, risk-taker, turtle), auto-mixed per game into nine playing styles. The profile is a set of 0–1 knobs that shape every decision: buying, building, trading, auction bidding and jail bail.
// packages/game-engine/src/bot/profile.ts
export type Difficulty = 'easy' | 'medium' | 'hard';
export type Archetype =
| 'monopolist' // hoards a colour group, denies opponents' sets
| 'specialist' // commits to one or two groups
| 'vindictive' // piles pressure on the net-worth leader
| 'risktaker' // bids high, plays the Jackpot
| 'turtle'; // builds slowly, hoards cash
export interface BotProfile {
difficulty: Difficulty;
archetype: Archetype;
aggression: number; // 0..1 — buy/bid/build appetite
tradeAppetite: number; // 0..1 — how often it proposes deals
}The profile rides on PlayerState.botProfile, baked into the game’s engine_state at start — exactly like the team field — so there was no database migration. An easy bot with no profile is byte-for-byte the original opponent, which is why every prior test still passes.
A hard bot will pay over the list price to finish a monopoly, capped so auctions still resolve:
// over-list bid, bounded so the auction always terminates
const maxBid = Math.round(listPrice * (1 + 0.4 * profile.aggression));Everything stays deterministic and guaranteed to finish: bot trades are strictly gated (one open offer at a time, seeded frequency, only offers a recipient would accept), and the simulator was stress-tested across 2–6 players over 400 randomized fast-check runs. Engine 282 / daemon 91 tests green.
