-- Spatial Prisioner's Dillema -- -- Example of spatial prisioner's dillema -- -- Cooperators can survive by forming clusters and thereby outweighing losses against defectors. -- There is a balance between payoff and survival rates -- The following example uses the following rules: -- 1. Each player occupies a single cell -- 2. Players compete against all 8 neighbors in an 3 x 3 cell -- 3. The initial configuration has one defector in the center -- 4. Updates are synchronous -- 5. Each cell copies the most successful strategy in its neighborhood -- This game leads to fascinating spatio-temporal patterns. -- Paper reference = Nowak MA & May RM (1992). Evolutionary games and spatial chaos. -- Nature 359:826–829 -- Strategies and transitions COOPERATE = 1 DEFECT = 2 COOPERATE_TO_DEFECT = 3 DEFECT_TO_COOPERATE = 4 -- Payoffs -- (T)emptation > (R)eward > (P)unishment > (S)ucker's payoff -- T > R > P > S is the condition for prisioner's dillema T = 1.85 R = 1 P = 0.01 S = 0 -- lattice size SIZE = 99 -- number of simulations TURNS = 300 -- percentage of defectors percent_defectors = 0 -- === functions === -- initCellSpace -- initial configuration of the spatial game -- function initCellSpace (cs) middle = (SIZE - 1)/2 forEachCell(cs, function(cell) cell.strategy = COOPERATE if (cell.x == middle and cell.y == middle) then cell.strategy = DEFECT end end) end -- playGame -- prisioner's dillema, two players, one iteration -- function playGame(p1, p2) if p1.strategy == COOPERATE and p2.strategy == COOPERATE then return {R,R} end if p1.strategy == DEFECT and p2.strategy == COOPERATE then return {T,S} end if p1.strategy == COOPERATE and p2.strategy == DEFECT then return {S,T} end if p1.strategy == DEFECT and p2.strategy == DEFECT then return {P,P} end end -- playAgainstAllNeighbors -- play the dillema against all neighbors -- calculate the cell's payoff function playAgainstAllNeighbors(cell) cell.payoff = 0 forEachNeighbor(cell, function(cell, neigh) result = playGame(cell, neigh) cell.payoff = cell.payoff + result[1] end ) end -- chooseBestStrategy -- player copies the best strategy among its neighbors -- function chooseBestStrategy(cell) local bestpayoff = cell.payoff cell.beststrategy = cell.strategy forEachNeighbor(cell, function (cell, neigh) if (cell ~= neigh) then if neigh.payoff > bestpayoff then bestpayoff = neigh.payoff cell.beststrategy = neigh.strategy end end end) end -- changeCell -- update strategy -- define transitions for visualization purposes function changeCell (cell) if (cell.strategy == COOPERATE) and (cell.beststrategy == DEFECT) then cell.strategy = COOPERATE_TO_DEFECT end if (cell.strategy == DEFECT) and (cell.beststrategy == COOPERATE) then cell.strategy = DEFECT_TO_COOPERATE end end -- updateCell -- after visualizing the transitions, update the cell space function updateCell(cell) if cell.strategy == DEFECT_TO_COOPERATE then cell.strategy = COOPERATE end if cell.strategy == COOPERATE_TO_DEFECT then cell.strategy = DEFECT end end -- statsCS -- calculate statistics on the cell space function statsCS(cs) local num_defectors = 0 forEachCell (cs, function (cell) if cell.strategy == DEFECT then num_defectors = num_defectors + 1 end end) percent_defectors = (100*num_defectors)/(SIZE*SIZE) return percent_defectors end -- define the cell space cs = CellularSpace{xdim = SIZE, ydim = SIZE} -- define a global cell to collect game statistics global = Cell{percent_defectors = 0} -- initialize the cell space initCellSpace (cs) -- create a 3 x 3 neighborhood that wraps around the edges createMooreNeighborhood(cs, "1", true) -- define a map legend mylegend = Legend{ type = "number", groupingMode = "uniquevalue", maximum = 4, minimum = 1, colorBar = { {color = "blue", value = COOPERATE}, {color = "red", value = DEFECT}, {color = "yellow", value = COOPERATE_TO_DEFECT}, {color = "green", value = DEFECT_TO_COOPERATE} } } -- define a map observer Observer{ subject = cs, type = "map", attributes = {"strategy"}, legends = {mylegend} } -- define a chart observer Observer{ subject = global, type = "chart", attributes = {"percent_defectors"}, title = "Percentage of Defectors", curveTitle = "defect x time", curveLabel = "%defect", xLabel = "time", yLabel = "percent (1-100)" } cs:notify() -- show the initial state -- play the game for i = 1, TURNS do forEachCell(cs, updateCell) global.percent_defectors = statsCS (cs) global:notify (i) -- show chart with evolution of defectors forEachCell(cs, playAgainstAllNeighbors) forEachCell(cs, chooseBestStrategy) forEachCell(cs, changeCell) cs:notify() -- show map of cell space end