Complexity of Cooperation Web Site

Simple Version of Culture Model Source Code (Pascal)


program CultureDemo1;
{Axelrod's Cultural Demonstration Program}
{   begun 10/17/95 for PS 794}
{See "Cultural Demo Documentation" for details}

{Ver1, begun 10/17/95}
{       Allows only 4 neigbhors}
{       Allows maximum of 20x20 territory}


    const
{control constants}
        Version = 1;                                        {Program Version Number}
        old_random_seed = 0;        {0 means new seed, else enter an old seed to reuse}
        cyclemax = 200;         {number of cycles[events] in each reporting period. Cylce is one active actor.}
        periodmax = 10;             {number of periods of cyclemax each.}
        popmax = 1;                 {number of populations, each with cyclemax*periodmax active actors}
        write_cultures = True;  {if true, report each individual's culture each period}
        write_distances = True; {if true, report cultureal distance between adjacent sites}

{input parameters}
        Xmax = 5;                       {Size of land, west to east.}
        Ymax = 5;                       {Size of land, north to south.}
        bitmax = 5;                 {Traits. Size of chromosome, i.e. number of bits in indiv's culture. }
        allelemax = 10;             {Number of traits per feature}


    type
        x_type = 1..xmax;                           {range of  indiv's x coord}
        y_type = 1..ymax;                           {range of indiv's y coord}
        bit_type = 1..bitmax;                       {bit on culture}
        culture_array = array[0..21, 0..21, 1..bitmax] of integer;  {for x, y,bit}
  {max actual geography is 20x20}
        direction_type = 1..4;                          {4 neighbors; north, s, e, w order}
        direction_array = array[1..4] of integer;                   {for x and y moves}
    var
        initial_datetime, end_datetime: datetimerec;    {date, etc.}
        initial_hour, end_hour: longint;
        start_time, end_time, duration: longint;    {for calc of run's duration}
        random_seed: integer;
        pop: integer;                                       {current population number, 1...popmax}
        period: integer;                                    {current period number, 1..periodmax}
        cycle: integer;                                     {current cycle (ie active indiv), 1..cyclemax}
        ix: x_type;                                 {x coordinate of current active individual}
        iy: y_type;                                 {y coordinate of current active individual}
        jx: integer;                                    {x coord of a neighbor, 0..xmax+1}
        jy: integer;                                    {y coord of a neighbor, 0..ymax+1}
        culture: culture_array;                     {culture of ix,iy,bit - an integer variable}
            {-1 for beyond bord, or 0 or 1 for internal indiv's culture}
            {x=0 is beyond internal left border, x=xmax+1 is beyond right border, etc.}
            {This allows indivs on internal border to get automatic non-matches with illegal neighbors}
        xmove, ymove: direction_array;              {jointly defines North, East, West, South direction}
        bit: bit_type;                                      {the current bit of the culture}
        neighbor: direction_type;                       {neighbor, 1 to 4}
        direction: direction_type;                      {4 neighbors; north, s, e, w order}
        event: integer;                                     {cumulative count of events in this pop}
        changes_this_period: integer;                   {count of changes in this period}

  { ---------------------------------------------------------------  }
    function random_one_to_n (n: longint): longint;     {Same as in Tipping4 Program}
                            {proc returns a random number between 1 and n; modified from Bennett's random_range}
        var
            ub, lb: integer;
            r: integer;
    begin                                                       {random gives # betw -32768 and 32767}
        ub := 32767 - (32767 mod n);
        lb := -32768 - (-32768 mod n);                  {truncate distrib on 2 ends so that later mod is OK}
        repeat
            r := random;
        until (r <= ub) and (r >= lb);                      {make sure random genrated is in truncated (even) distrib}
        random_one_to_n := abs(r mod n) + 1;
    end;            {random function}
 { ---------------------------------------------------------------  }
    procedure set_random_seed;          {Same as in Tipping4 Program}
    begin
        if old_random_seed = 0 then
            begin                                               {generate new seed}
                random_seed := initial_datetime.hour + initial_datetime.minute + initial_datetime.second;
                random_seed := random_seed + (initial_datetime.second * 300);
                random_seed := random_seed + (initial_datetime.minute * initial_datetime.hour);
                random_seed := random_seed + (initial_datetime.minute * initial_datetime.second);
                randseed := random_seed;                {set system's random seed}
            end
        else
            begin                                       {use old seed, which was inputed as constant}
                randseed := old_random_seed;
            end;
    end; {set_random_seed;}
 { ---------------------------------------------------------------  }
    procedure Initialize_run;
        var
            x, y, bit: integer;
    begin
        gettime(initial_datetime);
        initial_hour := initial_datetime.hour;              {to force long int}
        start_time := 60 * 60 * initial_hour + 60 * initial_datetime.minute + initial_datetime.second;
        Writeln('Axelrod''s Cultural Demo Program, Version ', Version : 4);
        Write('   This run begun on ', initial_datetime.month : 2, '/', initial_datetime.day : 2);
        Writeln('/', initial_datetime.year : 4, ' at ', initial_datetime.hour : 2, ':', initial_datetime.minute : 2, '.');
        Writeln(cyclemax : 5, ' cycles in each reporting period');
        Writeln(periodmax : 5, '    periods in each population');
        Writeln(popmax : 5, '   population');
        Writeln(xmax : 5, ' width of land, east to west (number of cols.)');
        Writeln(ymax : 5, ' width of land, north to south (number of rows)');
        Writeln(bitmax : 5, '   traits in culture string');
        Writeln(allelemax : 5, '    features per trait');
        set_random_seed;
        writeln;
        for x := 1 to xmax do                           {initialize ind's beyond the internal borders. Never changes.}
            begin
                for bit := 1 to bitmax do
                    begin
                        culture[x, 0, bit] := -1;           {initial culture for beyond top internal border}
                        culture[x, ymax + 1, bit] := -1;            {initial culture for beyond bottom internal }
                    end;
            end; {x}
        for y := 1 to ymax do                           {initialize indiv's beyond the internal borders. Never changes.}
            begin
                for bit := 1 to bitmax do
                    begin
                        culture[0, y, bit] := -1;           {initial culture for beyond left internal border}
                        culture[Xmax + 1, y, bit] := -1;            {initial for beyond right internal border}
                    end;
            end;{y}
        Xmove[1] := 0;                  {define North, in seeking neighbors}
        Ymove[1] := -1;
        Xmove[2] := 1;                  {define East}
        Ymove[2] := 0;
        Xmove[3] := -1;                 {define West}
        Ymove[3] := 0;
        Xmove[4] := 0;                  {define South}
        Ymove[4] := 1;
    end; {initialize run procedure}

  { ---------------------------------------------------------------  }
    procedure interact;                 {i converges one bit to j if possible}
        var
            try_another_bit: boolean;       {control on whether still searching for another bit that differs}
            bit_count: integer;             {count so as to give up when all bit tried, ie x=y}
            bit_try: integer;               {bit being tried looking for dissimilarity}
    begin
        bit_try := random_one_to_n(bitmax);         {initial bit location tried}
        try_another_bit := true;
        bit_count := 1;
        repeat
            if culture[ix, iy, bit_try] <> culture[jx, jy, bit_try] then
                begin
                    culture[ix, iy, bit_try] := culture[jx, jy, bit_try];       {i converges since unequal on current bit}
                    changes_this_period := changes_this_period + 1;     {a change took place}
                    try_another_bit := false;                               { done with search }
                end
            else
                begin
                    bit_count := bit_count + 1;
                    bit_try := (bit_try + 1) mod bitmax + 1;
                    if bit_count > bitmax then                      {give up because just tried all bits}
                        try_another_bit := false;
                end;
        until try_another_bit = false;
    end;{interact procedure}
  { ---------------------------------------------------------------  }
    procedure Report_Cultures;          {from output_period procedure}
        var                                                     {reports first five bits of culture}
            xtemp, ytemp: integer;
            bit_temp: integer;
    begin
        for ytemp := 1 to ymax do
            begin
                write(' y=', ytemp : 3, '. Culture = ');
                for xtemp := 1 to xmax do
                    begin
                        for bit_temp := 1 to bitmax do
                            begin
                                write(culture[xtemp, ytemp, bit_temp] : 1);
                            end;{bit_temp}
                        write('  ');        {space between individuals}
                    end;{ixtemp}
                writeln;
            end; {iytemp}
    end;
  { ---------------------------------------------------------------  }
    procedure Calc_and_Report_Distance;                 {cultural distances with output}
        type
            row_array = array[1..xmax] of integer;          {for output of distances}
        var
            xtemp, ytemp: integer;          {geo loc}
            itemp: integer;                     {bit position}
            X_distance: row_array;          {cultural distance between (x,y) to (x+1,y), ie to right }
            Y_distance: row_array;          {cultural distance between (x,y) to (x,y+1), ie down }
    begin
        for ytemp := 1 to ymax do
            begin
                for xtemp := 1 to xmax do
                    begin
                        X_distance[xtemp] := 0;             {calcs will be for one row at a time}
                        Y_distance[xtemp] := 0;
                        for itemp := 1 to bitmax do
                            begin                                           {increment distance on x axis, then y axis}
                                if xtemp < xmax then                        {to avoid going off right side}
                                    if culture[xtemp, ytemp, itemp] <> culture[xtemp + 1, ytemp, itemp] then
                                        X_distance[xtemp] := X_distance[xtemp] + 1;
                                if (ytemp < ymax) then                  {only do down calcs if not last row}
                                    begin
                                        if (culture[xtemp, ytemp, itemp] <> culture[xtemp, ytemp + 1, itemp]) then
                                            Y_distance[xtemp] := Y_distance[xtemp] + 1
                                    end; {if not last row}
                            end; {itemp loop for culture bits}
                    end; {xtemp loop for calc distances}
                if write_distances then
                    begin
                        write('y= ', ytemp : 3, '. Dis across: ');
                        for xtemp := 1 to xmax - 1 do
                            begin  {write row for across distances}
                                write(' ', X_distance[xtemp] : 3);
                            end; {xtemp for writing row for across distances}
                        writeln;
                        if (ytemp < ymax) then
                            write('y= ', ytemp : 3, '. Dis down: ');
                        if (ytemp < ymax) then
                            begin
                                for xtemp := 1 to xmax do
                                    begin  {write row for distances down, only if not last row}
                                        write(' ', Y_distance[xtemp] : 3);
                                    end; {xtemp for writing row for  distances down}
                                writeln;
                            end; {if not last row}
                    end;{if write_distances}
            end;{ytemp}
        writeln;
    end;
  { ---------------------------------------------------------------  }
    procedure Periodic_Output;
    begin
        writeln('Event', event : 5, '. Changes this period', changes_this_period : 5, '.');
        if write_cultures then
            Report_Cultures;                                    {first output of a period}
        Calc_and_Report_Distance;                   {second output of a period}
    end;
 { ---------------------------------------------------------------  }
    procedure Initialize_pop;
        var
            x, y: integer;                                      {local variable for coord of an individual}
            bit: integer;                                       {local variable for number of gene on the cultrual chormosome}
    begin
        writeln('Pop ', pop : 3, ':');
        for x := 1 to xmax do
            begin
                for y := 1 to ymax do
                    begin
                        for bit := 1 to bitmax do
                            begin
                                culture[x, y, bit] := Random_One_to_N(allelemax) - 1;
                    {initial culture for internal places}
                            end;
                    end;
            end; {x loop}
        changes_this_period := 0;                           {Needed here for pop >1}
        Periodic_Output;
    end; {initialize_pop procedure}
  { ---------------------------------------------------------------  }
    procedure Output_Run;                       {Output for run - last thing written}
    begin
        gettime(end_datetime);
        end_hour := end_datetime.hour;          {to force long interger}
        end_time := 60 * 60 * end_hour + 60 * end_datetime.minute + end_datetime.second;
        duration := end_time - start_time;
        Writeln('Duration of this run is ', duration : 5, ' seconds.');
    end;
 { ---------------------------------------------------------------  }
 {- - - - - - - - - - - - - - - - - ----------------- - - - - - - - - - - - - - - - - }
 { ---------------------------------------------------------------  }
{M A I N    P R O G R A M }
begin
    initialize_run;                                                             {initialize whole run}
    for pop := 1 to popmax do
        begin
            event := 0;                                                         {count of events in this pop}
            Initialize_pop;                                                 {INITIALIZE current population}
            for period := 1 to periodmax do                          {REPORTING PERIOD }
                begin
                    changes_this_period := 0;                           {count of changes in this period}
                    for cycle := 1 to cyclemax do                   {CYCLE of one active actor}
                        begin
                            event := event + 1;                             {every cycle is an event}
                            ix := Random_one_to_n(Xmax);            {select X coord of active actor}
                            iy := Random_one_to_n(Ymax);            {select Y coord of active actor}
                            bit := random_one_to_n(bitmax);     {first bit to be checked for match}
                            repeat                                              {get an internal direction}
                                direction := random_one_to_n(4);        {select one of four directions for interaction}
                                jx := ix + xmove[direction];                {cacl  coords of selected neighbor}
                                jy := iy + ymove[direction]
                            until culture[jx, jy, bit] <> -1;           {check not gotten a neighbor outside of region}
                            if (culture[ix, iy, bit] = culture[jx, jy, bit]) then  {match on selected bit}
                                interact;                                           {including count if a change occured}
                        end; { cycle of one active }
                    Periodic_Output;
                end;{pop cycle}
        end; {run}
    Output_Run;                                         {output of the run}
end.{main program}

Back to Cultural Model Page
Back to Chapter 7
Back to Appendix A
Back to Appendix B
Back to Complexity of Cooperation Home Page

University of Michigan Center for the Study of Complex Systems
Contact cscs@umich.edu.
Revised November 4, 1996.