Zone of Cooperation
Reference - <Zones of Cooperation in Demographic Prisoner's Dilemma, Joshua M. Epstein>
http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.8.8629&rep=rep1&type=pdf
Everyone in this population is one of two types - cooperator(Green color) or defector(Red color). Agents move and play game, and payoffs accumulate. If accumulated payoff exceed some threshold, agents clone offspring of the same strategy(C-> C, D-> D). Agents have life(20 frames).
* * * Reward Table (Row Player, Column Player)
C D
C (R, R) (S, T)
D (T, S) (P, P)
/**
* Copyright greentec ( http://wonderfl.net/user/greentec )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/UUPT
*/
package {
import flash.display.Sprite;
import com.bit101.components.Label;
import com.bit101.components.NumericStepper;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Shape;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.KeyboardEvent;
import flash.geom.Rectangle;
public class FlashTest extends Sprite {
public var bitmap:Bitmap;
public var bitmapData:BitmapData;
public var gridWidth:int = 13;
public var gridHeight:int = 13;
public var gridWidthNum:int = 30;
public var gridHeightNum:int = 30;
public var indentX:int;
public var indentY:int;
public var colorObject:Object = {
"C" : 0x1EC266,
"D" : 0x831322
}
public var playerArray:Array;
public var population:Vector.<Vector.<Player>> = new Vector.<Vector.<Player>>(gridWidthNum);
public var grid:Vector.<Vector.<Boolean>> = new Vector.<Vector.<Boolean>>(gridWidthNum);
public var rewardObject:Object = {
"T" : -6,
"R" : -5,
"P" : 2,
"S" : 6
}
public var initialLife:int = 25;
public var reproduceThreshold:int = 10;
public var statsLabel:Label;
public function FlashTest() {
// write as3 code here..
//initialize graphics
var background:BitmapData = new BitmapData(465, 465, false, 0x292929);
addChild(new Bitmap(background));
bitmapData = new BitmapData(465, 465, true, 0xff292929);
bitmap = new Bitmap(bitmapData);
addChild(bitmap);
//grid
var grid:Shape = new Shape();
grid.graphics.lineStyle(0, 0xcccccc, 0.5);
var i:int;
for (i = 0; i <= gridWidthNum; i += 1)
{
grid.graphics.moveTo(0, i * gridHeight);
grid.graphics.lineTo(gridWidth * gridWidthNum, i * gridHeight);
}
for (i = 0; i <= gridHeightNum; i += 1)
{
grid.graphics.moveTo(i * gridWidth, 0);
grid.graphics.lineTo(i * gridWidth, gridHeight * gridHeightNum);
}
grid.x = (465 - gridWidth * gridWidthNum) / 2;
grid.y = (465 - gridHeight * gridHeightNum) / 2;
addChild(grid);
indentX = (465 - gridWidth * gridWidthNum) / 2;
indentY = (465 - gridHeight * gridHeightNum) / 2;
makeInitialPopulation();
drawGraphics();
//UI
var label:Label;
var numericStepper:NumericStepper;
label = new Label(this, 10, 440, "T");
numericStepper = new NumericStepper(this, 20, 440, onRewardChanged);
numericStepper.name = "T";
numericStepper.value = parseInt(rewardObject.T);
numericStepper.width = 60;
label = new Label(this, 130, 440, "R");
numericStepper = new NumericStepper(this, 140, 440, onRewardChanged);
numericStepper.name = "R";
numericStepper.value = parseInt(rewardObject.R);
numericStepper.width = 60;
label = new Label(this, 250, 440, "P");
numericStepper = new NumericStepper(this, 260, 440, onRewardChanged);
numericStepper.name = "P";
numericStepper.value = parseInt(rewardObject.P);
numericStepper.width = 60;
label = new Label(this, 370, 440, "S");
numericStepper = new NumericStepper(this, 380, 440, onRewardChanged);
numericStepper.name = "S";
numericStepper.value = parseInt(rewardObject.S);
numericStepper.width = 60;
statsLabel = new Label(this, 280, 5, "");
addEventListener(Event.ENTER_FRAME, onGameLoop);
}
private function onRewardChanged(e:Event):void
{
rewardObject[e.target.name] = e.target.value;
}
private function onGameLoop(e:Event = null):void
{
makeGame();
makeChild();
makeMove();
drawGraphics();
//debugShape.graphics.clear();
//debugShape.graphics.lineStyle(0, 0xffffff);
var Ccount:int = 0;
var Dcount:int = 0;
var i:int;
var len:int = playerArray.length;
var player:Player;
for (i = 0; i < len; i += 1)
{
player = playerArray[i];
if (player.type == "C")
{
Ccount += 1;
}
else if (player.type == "D")
{
Dcount += 1;
}
}
statsLabel.text = "Cooperate Player :\t" + String(Ccount) + " (" + String(int(Ccount / (Ccount + Dcount) * 1000) / 10) + " %)" + "\nDefect Player :\t\t" + String(Dcount) + " (" + String(int(Dcount / (Ccount + Dcount) * 1000) / 10) + " %)";
}
private function makeChild():void
{
var i:int;
var len:int = playerArray.length;
var player:Player;
var childPlayer:Player;
var dir:String;
var newX:int;
var newY:int;
for (i = 0; i < len; i += 1)
{
player = playerArray[i];
if (player.wealth >= reproduceThreshold)
{
dir = findNeighbor(player, false); //find empty cell
if (dir != "")
{
dir = dir.substr(int(Math.random() * dir.length), 1);
switch(dir)
{
case "E":
newX = (player._x + 1) % gridWidthNum;
newY = player._y;
break;
case "W":
newX = (player._x - 1 + gridWidthNum) % gridWidthNum;
newY = player._y;
break;
case "S":
newX = player._x;
newY = (player._y + 1) % gridHeightNum;
break;
case "N":
newX = player._x;
newY = (player._y - 1 + gridHeightNum) % gridHeightNum;
break;
}
childPlayer = new Player(newX, newY, initialLife, player.type, player.wealth / 2);
population[newX][newY] = childPlayer;
grid[newX][newY] = true;
playerArray.push(childPlayer);
player.wealth /= 2;
}
}
}
}
private function makeGame():void
{
var i:int;
var j:int;
var len:int = playerArray.length;
var player:Player;
var player2:Player;
var dir:String;
for (i = 0; i < len; i += 1)
{
player = playerArray[i];
dir = findNeighbor(player, true); //find neighbor - occupied cell
if (dir != "")
{
dir = dir.substr(int(Math.random() * dir.length), 1);
switch(dir)
{
case "E":
player2 = population[(player._x + 1) % gridWidthNum][player._y];
break;
case "W":
player2 = population[(player._x - 1 + gridWidthNum) % gridWidthNum][player._y];
break;
case "S":
player2 = population[player._x][(player._y + 1) % gridHeightNum];
break;
case "N":
player2 = population[player._x][(player._y - 1 + gridHeightNum) % gridHeightNum];
break;
}
if (player.type == "C" && player2.type == "C")
{
player.wealth += rewardObject.P;
player2.wealth += rewardObject.P;
}
else if (player.type == "D" && player2.type == "C")
{
player.wealth += rewardObject.S;
player2.wealth += rewardObject.T;
}
else if (player.type == "C" && player2.type == "D")
{
player.wealth += rewardObject.T;
player2.wealth += rewardObject.S;
}
else if (player.type == "D" && player2.type == "D")
{
player.wealth += rewardObject.R;
player2.wealth += rewardObject.R;
}
}
}
}
private function findNeighbor(player:Player, occupied:Boolean):String
{
var dir:String;
dir = "";
if (grid[(player._x + 1) % gridWidthNum][player._y] == occupied)
{
dir += "E";
}
if (grid[(player._x - 1 + gridWidthNum) % gridWidthNum][player._y] == occupied)
{
dir += "W";
}
if (grid[player._x][(player._y + 1) % gridHeightNum] == occupied)
{
dir += "S";
}
if (grid[player._x][(player._y - 1 + gridHeightNum) % gridHeightNum] == occupied)
{
dir += "N";
}
return dir;
}
private function makeMove():void
{
var i:int;
var j:int;
var len:int = playerArray.length;
var player:Player;
var dir:String;
var randDir:String;
for (i = len - 1; i > -1; i -= 1)
{
player = playerArray[i];
player.life -= 1;
if (player.life <= 0)
{
playerArray.splice(i, 1);
grid[player._x][player._y] = false;
population[player._x][player._y] = null;
player = null;
continue;
}
dir = findNeighbor(player, false); //find empty neighbor cell - to move
if (dir != "")
{
randDir = dir.substr(int(Math.random() * dir.length), 1);
switch(randDir)
{
case "E":
grid[player._x][player._y] = false;
population[(player._x + 1) % gridWidthNum][player._y] = player;
population[player._x][player._y] = null;
player._x = (player._x + 1) % gridWidthNum;
grid[player._x][player._y] = true;
//population[player._x][player._y] = player;
break;
case "W":
grid[player._x][player._y] = false;
population[(player._x - 1 + gridWidthNum) % gridWidthNum][player._y] = player;
population[player._x][player._y] = null;
player._x = (player._x - 1 + gridWidthNum) % gridWidthNum;
grid[player._x][player._y] = true;
//population[player._x][player._y] = player;
break;
case "S":
grid[player._x][player._y] = false;
population[player._x][(player._y + 1) % gridHeightNum] = player;
population[player._x][player._y] = null;
player._y = (player._y + 1) % gridHeightNum;
grid[player._x][player._y] = true;
break;
case "N":
grid[player._x][player._y] = false;
population[player._x][(player._y - 1 + gridHeightNum) % gridHeightNum] = player;
population[player._x][player._y] = null;
player._y = (player._y - 1 + gridHeightNum) % gridHeightNum;
grid[player._x][player._y] = true;
break;
}
}
}
}
private function makeInitialPopulation():void
{
var initialCooperateNum:int = 50;
var initialDefectNum:int = 50;
var i:int;
var j:int;
var player:Player;
//initialize Grid
for (i = 0; i < gridWidthNum; i += 1)
{
grid[i] = new Vector.<Boolean>(gridHeightNum);
for (j = 0; j < gridHeightNum; j += 1)
{
grid[i][j] = false;
}
}
for (i = 0; i < gridWidthNum; i += 1)
{
population[i] = new Vector.<Player>(gridHeightNum);
for (j = 0; j < gridHeightNum; j += 1)
{
population[i][j] = null;
}
}
//population = [];
var initialPopulationIndex:Array = printRandomNumber(gridWidthNum * gridHeightNum, initialCooperateNum + initialDefectNum);
playerArray = [];
for (i = 0; i < initialCooperateNum; i += 1)
{
player = new Player(initialPopulationIndex[i] % gridWidthNum, int(initialPopulationIndex[i] / gridWidthNum), initialLife, "C");
population[initialPopulationIndex[i] % gridWidthNum][int(initialPopulationIndex[i] / gridWidthNum)] = player;
grid[initialPopulationIndex[i] % gridWidthNum][int(initialPopulationIndex[i] / gridWidthNum)] = true;
playerArray.push(player);
}
for (i = initialDefectNum; i < initialCooperateNum + initialDefectNum; i += 1)
{
player = new Player(initialPopulationIndex[i] % gridWidthNum, int(initialPopulationIndex[i] / gridWidthNum), initialLife, "D");
population[initialPopulationIndex[i] % gridWidthNum][int(initialPopulationIndex[i] / gridWidthNum)] = player;
grid[initialPopulationIndex[i] % gridWidthNum][int(initialPopulationIndex[i] / gridWidthNum)] = true;
playerArray.push(player);
}
}
private function drawGraphics():void
{
var i:int;
var j:int;
var len:int = playerArray.length;
var player:Player;
var _alpha:int;
bitmapData.fillRect(bitmapData.rect, 0xff292929);
for (i = 0; i < len; i += 1)
{
player = playerArray[i];
_alpha = Math.min((player.wealth + 30) / 60 * 255, 255);
bitmapData.fillRect(
new Rectangle(player._x * gridWidth + indentX, player._y * gridHeight + indentY, gridWidth, gridHeight),
_alpha << 24 | colorObject[player.type]
);
}
}
private function printRandomNumber(n:int, k:int) : Array
{
var original:Array=[];
var result:Array=[];
var i:int;
var randInt:int;
var temp:Object;
for(i=0;i<n;i+=1)
{
original.push(i);
}
for(i=0;i<k;i+=1)
{
randInt = Math.random() * (n-i) + i;
temp = original[i];
original[i] = original[randInt];
original[randInt] = temp;
result.push(original[i]);
}
return result;
}
}
}
Class
{
/**
* ...
* @author ypc
*/
class Player
{
public var life:int;
public var _x:int;
public var _y:int;
public var type:String;
public var wealth:int;
public function Player(_x:int, _y:int, life:int, type:String, wealth:int = 0)
{
this._x = _x;
this._y = _y;
this.life = life;
this.type = type;
this.wealth = wealth;
}
}
}