My theory was that a Monopoly player who remembers trades should beat one who does not. If a player tried to sell me the brown properties for $60 two turns ago, I can offer $45 now and expect a yes. I evolved two populations of players with identical training budgets, one with a memory of trades and mortgages and one without. The memory players won 56.1% of the games where two of each sat at the table. That looked like the answer until a third population, which barely used its one new input, beat the no-memory players by the same margin in the same two-against-two games.
The setup
The players are neural networks evolved with NEAT (NeuroEvolution of Augmenting Topologies). NEAT keeps a population of networks, and each generation the better ones breed and mutate, changing weights and adding connections and nodes. It groups similar networks into species so that new structure is not wiped out before it is tuned; these populations used a version of that grouping adjusted for large starting networks, described in the previous post in this series.
The game is a simplified four-player Monopoly written for the experiment. It has houses and hotels with the real limit of 32 and 12, mortgages, and all 32 Chance and Community Chest cards. Players can offer to buy or sell a property for cash at 0.5 to 2.5 times its printed price, or swap one property for another. There are no doubles and no auctions, and a bankrupt player's properties go back to the bank. A game still going after 200 rounds goes to the richest player; in the final generation of training, 84% of the memory population's games and 93% of the other's ended in bankruptcy instead.
Each network sees 44 inputs describing the board and the decision in front of it. It answers every question the game asks (buy this? build? accept this offer? offer how much?) with a yes or no and an amount.
- With memory: 13 of the inputs summarise history. They include how often this opponent has accepted my offers, whether they rejected an offer for this property and at what price, whether they tried to sell it in the last 20 rounds and for how much, and recent mortgages. Two more inputs read back two memory slots, which the network writes with two extra outputs at every decision.
- Without memory: the identical network with those 15 inputs frozen at "nothing happened" and the slots never written.
Both populations had 100 networks and trained for 30 generations by self-play, meaning the networks' opponents were other networks rather than scripted players. Each network played 200 games per generation, and its score was the fraction it won. Most games were against its own population; the rest included members of a hall of fame, the best network from each earlier generation, so that the population kept beating old strategies and not just its current neighbours. Both populations started from the same random seed, so the first generation was identical.
The champion of each population is its best-scoring network in the final generation. Each champion then played 2,000 games in each of several line-ups: against three copies of a scripted player, against three copies of the other population's champion, and two against two. One scripted player buys and builds everything it can. The "sensible" one buys and builds only while it keeps $300 in cash, and proposes a trade only to complete a color group of its own. With four players, 25% is an even share of wins, and a 2,000-game figure carries a standard error of about one percentage point.
The first result
| line-up | champion's share of wins |
|---|---|
| with memory, against 3 "sensible" players | 27.8% |
| without memory, against 3 "sensible" players | 29.1% |
| with memory, against 3 players that buy everything | 25.4% |
| without memory, against 3 players that buy everything | 24.6% |
| 1 with memory against 3 without | 33.0% |
| 1 without memory against 3 with | 21.6% |
| 2 with memory against 2 without, games won by the memory pair | 56.1% (50% is even) |
| with memory, all 15 memory inputs blanked, against 3 "sensible" players | 20.8% |
Against the scripted players memory made no difference. I expected that: they rarely trade and never try to sell, so there is little about them to remember. Head to head, the memory player won a third of its games and the memory pair won 56.1%. Blanking the memory champion's memory inputs dropped it from 27.8% to 20.8% against the scripted players, so it depended on them. A network fed inputs it never saw in training can fail for reasons unrelated to memory, though.
The lowball offers
I checked the motivating example directly. Four copies of each champion played 1,000 games at one table, and I split their offers to buy by whether the owner had tried to sell that property in the last 20 rounds. The price is the average offer as a multiple of the printed price.
| champion | offers after the owner tried to sell | all other offers |
|---|---|---|
| with memory | 49 offers at 0.52 times the price, 40 accepted (82%) | 69,155 at 0.51 times, 8,483 accepted (12%) |
| without memory | 74 offers at 1.33 times the price, 0 accepted | 49,965 at 1.05 times, 1,073 accepted (2%) |
The no-memory player cannot see sale attempts, so its split only reflects which properties tend to be put up for sale. The memory player did not offer less after a sale attempt. It already offered close to the 0.5 floor on everything, so the floor hides any price effect. What changed was acceptance: 82% against 12%. That fits the theory, but it rests on 49 offers. It is also partly a statement about the sellers, who were copies of the same champion and had recently tried to sell.
Over training, both populations learned to refuse nearly every offer to buy their property. The acceptance rate fell from 44.7% in the first generation to 5.0% in the thirtieth with memory, and from 34.3% to 5.3% without.
A memory that predicts
Hand-picked memory features are a guess about what matters. The next test was whether a model reading the raw history could predict an opponent's answer better. I recorded every trade offer from 1,000 games between the two champions and split them by game: 700 games for training the predictors, 300 held back for testing them on games they had never seen. The training games held 137,561 offers for the first seed and 149,307 for the second, with 4.0% and 3.5% accepted. Each predictor used only information the player making the offer could have seen, and was trained and tested on samples balanced to half accepted and half rejected.
The four predictors differ only in what they are given. The first three are ordinary feed-forward networks. The fourth is an LSTM, a recurrent network that reads events one at a time and carries a state between them. The score is AUC: the chance that the predictor ranks a random accepted offer above a random rejected one, so 0.5 is guessing and 1 is perfect.
| predictor | test AUC, seed 1 | test AUC, seed 2 |
|---|---|---|
| the offer alone, no history | 0.873 | 0.875 |
| the offer plus the opponent's last 20 events, as 300 separate inputs | 0.963 | 0.964 |
| the offer plus 12 of the hand-made memory features | 0.965 | 0.961 |
| the LSTM reading the last 20 events in order, then the offer | 0.981 | 0.982 |
History matters a lot: without it the predictor managed 0.87, and with it every model was above 0.96. The flattened events and the hand-made features came out level, and the LSTM beat both on both seeds, so reading the events as a sequence added something that having them all at once did not. On the balanced test sets the LSTM was right 93.6% of the time on both seeds. (The hand-made set is 12 features rather than 13 because the prediction script left out one of them, the proposer's own recent mortgages.)
This table is a rerun. A review of this post found that in the first version of the prediction script, two of the 15 numbers describing each event, "is this the same property?" and "how long ago was it?", sat after a code comment on the same line and were always 0. The first run gave the LSTM 0.978 and 0.979 and the flattened events 0.953 and 0.959, which had made the flattened control look worse than the hand-made features. With the fix it is not.
The population that barely used its input
Next I trained a third population, C, with the same budget and seed as the first two. Its hand-made memory inputs were frozen and its memory slots switched off, as in the no-memory population. One input, "how often this player accepts my offers", was replaced by the LSTM's predicted probability that the current offer would be accepted. That input was live only when the network was deciding whether to propose a trade.
| line-up | C | memory population, for reference |
|---|---|---|
| against 3 "sensible" players | 25.0% | 27.8% |
| 1 against 3 of the no-memory population | 28.2% | 33.0% |
| 2 against 2 of the no-memory population, pair's share | 55.8% | 56.1% |
| 2 C against 2 of the memory population, C pair's share | 51.4% |
In two-against-two games C beat the no-memory population by the same margin as the memory population, and it held even with the memory pair. One against three it did less well, 28.2% against 33.0%. Then I froze its LSTM input at 0.5 to see how much it depended on it. Against the scripted players it went from 25.0% to 24.9%, and one live C against three frozen copies won 25.9%. C was barely using its one new input.
There are at least two reasons it might have ignored a good predictor. The network chooses its offer price in the same step that reads the prediction, so the prediction was always for an offer at the printed price, not the one about to be made. And C used the LSTM from the first, buggy run: in play it received the two event features that had been 0 in its training, so its predictions during C's games were not the ones measured in the table. I have not retrained C.
That changes what the first result means. In practice C is one more population with next to no memory. It differs from the no-memory population in one input it barely uses and in the path its evolution happened to take. Yet in the two-against-two games it beat the no-memory champion 55.8% to the memory champion's 56.1%. One reading is that the no-memory run simply produced a weaker champion than the other two runs did. With one training run per setup, I cannot separate that from a genuine benefit of memory. The 2,000-game line-ups measure these particular champions precisely. They say nothing about how much champions vary from one training run to the next, and that variation is what needed measuring.
What would settle it
- Several seeds per population. Three or more independent training runs for each setup, compared by the spread of their head-to-head results rather than one champion against one champion.
- Ask the predictor about the price being offered. Giving the network the predicted acceptance at several prices would make the prediction usable for the decision that matters.
- Retrain C with the fixed predictor, and retrain the predictor on C's own games. The LSTM learned from the first two champions, whose habits C's opponents did not share.
The code is a learning project and is not public. I wrote the NEAT implementation. Claude Code wrote the Monopoly simulator, the training and comparison scripts and the prediction experiment, and the LSTM code came from another Claude Code session. Every figure here comes from those scripts' saved output.
A predictor that was right 93.6% of the time did not make a better player, and a two-against-two margin that looked like the effect of memory showed up in a population with next to none. The experiment's direct evidence for the theory is 49 lowball offers, and I would want many more before believing it.
Top comments (0)