//=========================================================================== // A FRAMEWORK FOR ITERATED PRISONERS DILEMMA // May 2004 //=========================================================================== #include #pragma hdrstop #include "Unit1.h" // The following two lines are needed to enable the randomizer #include "stdlib.h" #include "time.h" //--------------------------------------------------------------------------- #pragma package(smart_init) #pragma resource "*.dfm" TForm1 *Form1; //--------------------------------------------------------------------------- __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { // Double buffering eliminates some screen flicker... DoubleBuffered = true; } //=========================================================================== // GLOBAL VARIABLES //=========================================================================== // ------------------------------------------------- your program begins here #define NO_DATA 99 // the word EMPTY will be replaced by 99 #define COOPERATE 0 #define DEFECT 1 #define ALWAYS_COOPERATE 0 #define ALWAYS_DEFECT 1 #define ALTERNATE 2 #define RANDOM 3 #define TIT_FOR_TAT 4 #define TIT_FOR_TWO_TATS 5 TShape *shape[24] = {0}; // sets up an array of shapes class anAgent { // sets up an array of agents public: //int cellX; //int cellY; int strategy; int score; int lastChoice; int nextToLastChoice; int othersLastChoice[24]; int othersNextToLastChoice[24]; } agent[24]; int chosenShape; bool aShapeWasChosen = false; int chosenCellX, chosenCellY; int cellX, cellY; int emptyE, emptyS; int fromCellX, fromCellY; bool stop=false; bool step=false; bool pause=false; int thisWorld[10][10]; int nextWorld[10][10]; int thisAgent, thatAgent; int thisAgentsChoice, thatAgentsChoice; int strategy; int nextAgent = 0; int lastThisAgent, lastThatAgent; int maxScore, minScore, range; bool agentsAreSet = false; int east, south, relativeE, relativeS, wrappedE, wrappedS; int neighbors; int iterations=0; //=========================================================================== // FUNCTIONS //=========================================================================== //--------------------------------------------------------------- empty world void emptyWorld (void) { for (int i = 0; i < 10; i++) { for (int j = 0; j < 10; j++) { thisWorld[i][j]= NO_DATA; } } } //--------------------------------------------------------------------- reset void reset (void) { // initialize everything... iterations = 0; nextAgent = 0; for (int i = 0; i < 24; i++) { agent[i].strategy = NO_DATA; agent[i].score = 0; agent[i].lastChoice = NO_DATA; agent[i].nextToLastChoice = NO_DATA; for (int j = 0; j < 24; j++) { agent[i].othersLastChoice[j] = NO_DATA; agent[i].othersNextToLastChoice[j] = NO_DATA; } } agentsAreSet = false; Form1->EditNextAgent->Text = 0; } //---------------------------------------------------------------- color ramp int colorRamp(int range, int value) { int pixelDistanceAlongPath = (value * 1792) / range; int red, green, blue; // Which edge of the color cube are we on? if (pixelDistanceAlongPath < 256) { // Edge 1 from BLACK to BLUE red=0; green=0; blue=pixelDistanceAlongPath; } else if (pixelDistanceAlongPath < 512) { // Edge 2 from BLUE to CYAN red =0; green=pixelDistanceAlongPath-256; blue=255; } else if (pixelDistanceAlongPath < 768) { // Edge 3 from CYAN to GREEN red =0; green =255; blue= 255-(pixelDistanceAlongPath-512); } else if (pixelDistanceAlongPath < 1024) { // Edge 4 from GREEN to YELLOW red= (pixelDistanceAlongPath-768); green =255; blue =0; } else if (pixelDistanceAlongPath <1280) { // Edge 5 from YELLOW to RED red =255; green=255-(pixelDistanceAlongPath-1024); blue =0; } else if (pixelDistanceAlongPath < 1536) { // Edge 6 from RED to MAGENTA red =255; green=0; blue=pixelDistanceAlongPath -1280; } else { // Edge 7 from MAGENTA to WHITE red =255; green=pixelDistanceAlongPath-1536; blue =255; } return (RGB(red, green, blue)); } //------------------------------------------------------------ set next agent void setNextAgent (int strategy) { if (nextAgent < 24) { switch (strategy) { case 0: { agent[nextAgent].strategy = ALWAYS_COOPERATE; agent[nextAgent].lastChoice = COOPERATE; agent[nextAgent].nextToLastChoice = COOPERATE; shape[nextAgent]->Shape = stCircle; break; } case 1: { agent[nextAgent].strategy = ALWAYS_DEFECT; agent[nextAgent].lastChoice = DEFECT; agent[nextAgent].nextToLastChoice = DEFECT; shape[nextAgent]->Shape = stSquare; break; } case 2: { agent[nextAgent].strategy = ALTERNATE; agent[nextAgent].lastChoice = random(2); agent[nextAgent].nextToLastChoice = random(2); shape[nextAgent]->Shape = stRoundSquare; break; } case 3: { agent[nextAgent].strategy = RANDOM; agent[nextAgent].lastChoice = random(2); agent[nextAgent].nextToLastChoice = random(2); shape[nextAgent]->Shape = stRoundSquare; break; } case 4: { agent[nextAgent].strategy = TIT_FOR_TAT; agent[nextAgent].lastChoice = random(2); agent[nextAgent].nextToLastChoice = random(2); shape[nextAgent]->Width = 40; shape[nextAgent]->Shape = stEllipse; break; } case 5: { agent[nextAgent].strategy = TIT_FOR_TWO_TATS; agent[nextAgent].lastChoice = random(2); agent[nextAgent].nextToLastChoice = random(2); shape[nextAgent]->Height = 40; shape[nextAgent]->Shape = stEllipse; break; } } shape[nextAgent]->Brush->Color = static_cast(colorRamp(1500, agent[nextAgent].score)); nextAgent++; Form1->EditNextAgent->Text = nextAgent; if (nextAgent == 24) { Form1->EditNextAgent->Text = "FULL"; agentsAreSet = true; } } } //--------------------------------------------------------------- render grid void renderGrid (void) { Form1->Canvas->Pen->Color = clSkyBlue; for (east=0; east<11; east++) { Form1->Canvas->MoveTo(50 * east, 0); Form1->Canvas->LineTo(50 * east, 500); } for (south=0; south<11; south++) { Form1->Canvas->MoveTo(0, 50 * south); Form1->Canvas->LineTo(500, 50 * south); } } //------------------------------------------------------------ shuffle shapes void shuffleShapes (void) { Form1->Refresh(); emptyWorld(); Form1->EditIterations->Text = iterations; for (int i = 0; i < 24; i++) { cellX = random(10); cellY = random(10); while (thisWorld[cellX][cellY] != NO_DATA) { cellX = random(10); cellY = random(10); } thisWorld[cellX][cellY] = i; shape[i]->Left = 50 * cellX + 1; shape[i]->Top = 50 * cellY + 1; } } //------------------------------------------------------------- order shapes void orderShapes (void) { Form1->Refresh(); Form1->EditIterations->Text = iterations; for (int i = 0; i < 5; i++) { for (int j = 0; j < 5; j++) { if (i == 4 && j == 4) break; // there is no shape[24] shape[i * 5 + j]->Left = 50 * j + 1; shape[i * 5 + j]->Top = 50 * i + 1; } } } //-------------------------------------------------------------- color shapes void colorShapes (void) { for (int i = 0; i < 24; i++) { shape[i]->Pen->Color = static_cast(colorRamp(25, 24 - shape[i]->Tag)); shape[i]->Brush->Color = static_cast(colorRamp(25, shape[i]->Tag)); } } //--------------------------------------------------------------- size shapes void sizeShapes (void) { for (int i = 0; i < 24; i++) { shape[i]->Width = shape[i]->Tag * 2; shape[i]->Height = shape[i]->Tag * 2; } } //------------------------------------------------------------ outline shapes void outlineShapes (void) { for (int i = 0; i < 24; i++) { shape[i]->Pen->Width = shape[i]->Tag * .7; } } //------------------------------------------------------------- shape shapes void shapeShapes (void) { for (int i = 0; i < 24; i = i + 2) { shape[i]->Shape = stCircle; shape[i+1]->Shape = stSquare; } } //----------------------------------------------------------- restore shapes void restoreShapes (void) { for (int i = 0; i < 24; i++) { shape[i]->Pen->Color = clBlack; shape[i]->Brush->Color = clWhite; shape[i]->Width = 49; shape[i]->Height = 49; shape[i]->Pen->Width = 1; shape[i]->Shape = stCircle; } } //------------------------------------------------------- Cooperate or Defect int cooperateOrDefect (int who, int other) { int choice; switch (agent[who].strategy) { case ALWAYS_COOPERATE: { choice = COOPERATE; break; } case ALWAYS_DEFECT: { choice = DEFECT; break; } case ALTERNATE: { if (agent[who].lastChoice == DEFECT) choice = COOPERATE; if (agent[who].lastChoice == COOPERATE) choice = DEFECT; break; } case RANDOM: { choice = random(2); break; } case TIT_FOR_TAT: { if (agent[other].lastChoice == DEFECT) choice = DEFECT; if (agent[other].lastChoice == COOPERATE) choice = COOPERATE; break; } case TIT_FOR_TWO_TATS: { if (agent[other].lastChoice == DEFECT && agent[other].nextToLastChoice == DEFECT) choice = DEFECT; else choice = COOPERATE; break; } } return choice; } //---------------------------------------------------------------- Next Frame void nextFrame (void) { shape[lastThisAgent]->Pen->Color = clBlack; shape[lastThatAgent]->Pen->Color = clBlack; thisAgent = random(24); thatAgent = random(24); while (thisAgent == thatAgent) { thatAgent = random(24); } shape[thisAgent]->Pen->Color = clWhite; shape[thatAgent]->Pen->Color = clWhite; Form1->Refresh(); Form1->Canvas->Pen->Color = clWhite; Form1->Canvas->MoveTo(shape[thisAgent]-> Left + 25, shape[thisAgent]->Top + 25); Form1->Canvas->LineTo(shape[thatAgent]-> Left + 25, shape[thatAgent]->Top + 25); thisAgentsChoice = cooperateOrDefect(thisAgent, thatAgent); thatAgentsChoice = cooperateOrDefect(thatAgent, thisAgent); if (thisAgentsChoice == COOPERATE && thatAgentsChoice == COOPERATE) { agent[thisAgent].score += 3; agent[thatAgent].score += 3; } if (thisAgentsChoice == COOPERATE && thatAgentsChoice == DEFECT) { agent[thisAgent].score += 0; agent[thatAgent].score += 5; } if (thisAgentsChoice == DEFECT && thatAgentsChoice == COOPERATE) { agent[thisAgent].score += 5; agent[thatAgent].score += 0; } if (thisAgentsChoice == DEFECT && thatAgentsChoice == DEFECT) { agent[thisAgent].score += 1; agent[thatAgent].score += 1; } // This Agent agent[thisAgent].nextToLastChoice = agent[thisAgent].lastChoice; agent[thisAgent].lastChoice = thisAgentsChoice; agent[thisAgent].othersNextToLastChoice[thatAgent] = agent[thisAgent].othersLastChoice[thatAgent]; agent[thisAgent].othersLastChoice[thatAgent] = thatAgentsChoice; lastThisAgent = thisAgent; // That Agent agent[thatAgent].nextToLastChoice = agent[thatAgent].lastChoice; agent[thatAgent].lastChoice = thatAgentsChoice; agent[thatAgent].othersNextToLastChoice[thisAgent] = agent[thatAgent].othersLastChoice[thisAgent]; agent[thatAgent].othersLastChoice[thisAgent] = thisAgentsChoice; lastThatAgent = thatAgent; shape[thisAgent]->Hint = IntToStr(thisAgent) + " " + Form1->ListBoxNames->Items->Strings[thisAgent] + " " + Form1->ListBoxStrategies->Items->Strings[agent[thisAgent].strategy] + " " + IntToStr(agent[thisAgent].score); shape[thatAgent]->Hint = IntToStr(thatAgent) + " " + Form1->ListBoxNames->Items->Strings[thatAgent] + " " + Form1->ListBoxStrategies->Items->Strings[agent[thatAgent].strategy] + " " + IntToStr(agent[thatAgent].score); maxScore = 0; minScore = 1000000; for (int i = 0; i < 24; i++) { if (agent[i].score > maxScore) maxScore = agent[i].score; if (agent[i].score < minScore) minScore = agent[i].score; } range = maxScore - minScore + 2; for (int i = 0; i < 24; i++) { shape[i]->Brush->Color = static_cast (colorRamp(range, agent[i].score - minScore)); } iterations++; Form1->EditIterations->Text=iterations; Application->ProcessMessages(); } //=========================================================================== // EVENT HANDLERS //=========================================================================== //------------------------------------------------------------ On Form Create void __fastcall TForm1::FormCreate(TObject *Sender) { randomize(); reset(); // defines an array of shapes with properties and events for (int i = 0; i < 24; i++) { shape[i] = new TShape(this); shape[i]->Parent = Form1; shape[i]->Visible = true; shape[i]->OnMouseDown = ShapeMouseDown; shape[i]->OnMouseMove = ShapeMouseMove; shape[i]->Height = 49; shape[i]->Width = 49; shape[i]->Shape = stCircle; shape[i]->Tag = i; shape[i]->Hint = IntToStr(i) + " " + ListBoxNames->Items->Strings[i] + " " + IntToStr(agent[i].score); shape[i]->ShowHint = true; shape[i]->Pen->Width = 3; } emptyWorld(); shuffleShapes(); } //------------------------------------------------------------- On Form Paint // Redraws the grid anytime the windows is obscured and then uncovered void __fastcall TForm1::FormPaint(TObject *Sender) { renderGrid(); } //------------------------------------------------------- On Shape Mouse Down // An exception to the rule which says "Let Borland write event handlers." // This one you must type in yourself in addition to a reference to it // in Unit1.h (look at the source code in that unit). void __fastcall TForm1::ShapeMouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y) { TShape *shape = dynamic_cast(Sender); // Captures the index number of the shape that was clicked chosenShape = shape->Tag; // Remembers that an shape was chosen aShapeWasChosen = true; } //------------------------------------------------------- On Shape Mouse Move // An exception to the rule which says "Let Borland write event handlers." // This one you must type in yourself in addition to a reference to it // in Unit1.h (look at the source code in that unit). void __fastcall TForm1::ShapeMouseMove(TObject *Sender, TShiftState Shift, int X, int Y) { TShape *shape = dynamic_cast(Sender); // When the mouse moves over an shape // its index number is displayed EditTag->Text = shape->Tag; // the name associated with that shape is displayed EditName->Text = ListBoxNames->Items->Strings[shape->Tag]; } //-------------------------------------------------------- On Form Mouse Down void __fastcall TForm1::FormMouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y) { // Calculates the array indeces from the position of the mouse chosenCellX = X / 50; chosenCellY = Y / 50; // Only if the previous click was on an shape if (aShapeWasChosen) { // recalls the position of the shape that was previously clicked fromCellX = shape[chosenShape]->Left / 50; fromCellY = shape[chosenShape]->Top / 50; // moves the shape to the position that was just clicked shape[chosenShape]->Left = chosenCellX * 50 + 1; shape[chosenShape]->Top = chosenCellY * 50 + 1; // moves the agent in the array thisWorld[fromCellX][fromCellY] = 99; thisWorld[chosenCellX][chosenCellY] = chosenShape; // Another shape must be clicked before this will work again aShapeWasChosen = false; } } //-------------------------------------------------------- On Form Mouse Move // Calculates the array indices from the position of the mouse void __fastcall TForm1::FormMouseMove(TObject *Sender, TShiftState Shift, int X, int Y) { // Captures the indices of the cell of the array cellX = X / 50; cellY = Y / 50; // Displays the indices of the cell of the array EditX->Text = cellX; EditY->Text = cellY; } //+++++++++++++++++++++++++++++++++++++++++++++++++++ CREATE POPULATION PANEL //------------------------------------------------------ Set Cooperate Button void __fastcall TForm1::ButtonCooperateClick(TObject *Sender) { setNextAgent(ALWAYS_COOPERATE); } //--------------------------------------------------------- Set Random Button void __fastcall TForm1::ButtonRandomClick(TObject *Sender) { setNextAgent(RANDOM); } //--------------------------------------------------------- Set Defect Button void __fastcall TForm1::ButtonDefectClick(TObject *Sender) { setNextAgent(ALWAYS_DEFECT); } //---------------------------------------------------- Set Tit-for-Tat Button void __fastcall TForm1::ButtonTFTClick(TObject *Sender) { setNextAgent(TIT_FOR_TAT); } //------------------------------------------------------ Set Alternate Button void __fastcall TForm1::ButtonAlternateClick(TObject *Sender) { setNextAgent(ALTERNATE); } //----------------------------------------------- Set Tit-for-Two-Tats Button void __fastcall TForm1::ButtonTTTClick(TObject *Sender) { setNextAgent(TIT_FOR_TWO_TATS); } //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ CONTROL PANEL //---------------------------------------------------------------- Run Button void __fastcall TForm1::ButtonRunClick(TObject *Sender) { if (agentsAreSet) { stop = false; step = false; while (stop == false && step == false && iterations < 5000) { nextFrame(); } shape[thisAgent]->Pen->Color = clBlack; shape[thatAgent]->Pen->Color = clBlack; Form1->Refresh(); } } //--------------------------------------------------------------- Step Button void __fastcall TForm1::ButtonStepClick(TObject *Sender) { if (agentsAreSet) { step = true; nextFrame(); } } //--------------------------------------------------------------- Stop Button void __fastcall TForm1::ButtonStopClick(TObject *Sender) { stop = true; } //----------------------------------------------------- Shuffle Shapes Button void __fastcall TForm1::ButtonShuffleClick(TObject *Sender) { shuffleShapes(); } //------------------------------------------------------- Order Shapes Button void __fastcall TForm1::ButtonOrderClick(TObject *Sender) { orderShapes(); } //-------------------------------------------------------------- Reset Button void __fastcall TForm1::ButtonResetClick(TObject *Sender) { reset(); } //-------------------------------------------------------------- Clear Button void __fastcall TForm1::ButtonClearClick(TObject *Sender) { Refresh(); } //++++++++++++++++++++++++++++++++++++++++++++++++++ VISUALIZE INDEX AS PANEL //------------------------------------------------------- Color Shapes Button void __fastcall TForm1::ButtonColorClick(TObject *Sender) { colorShapes(); } //-------------------------------------------------------- Size Shapes Button void __fastcall TForm1::ButtonSizeClick(TObject *Sender) { sizeShapes(); } //----------------------------------------------------- Outline Shapes Button void __fastcall TForm1::ButtonOutlineClick(TObject *Sender) { outlineShapes(); } //------------------------------------------------------- Shape Shapes Button void __fastcall TForm1::ButtonShapeClick(TObject *Sender) { shapeShapes(); } //----------------------------------------------------- Restore Shapes Button void __fastcall TForm1::ButtonRestoreClick(TObject *Sender) { restoreShapes(); } //--------------------------------------------------------- That's All Folks