


















                    This example is a network that plays  Tic-Tac-Toe.
               It  incorporates  no  lookahead  and thus can be beaten
               with the one strategy that requires lookahead for  suc-
               cessful defence.

               _1._1._1.  _N_e_t_w_o_r_k _D_e_s_i_g_n

                    The design revolves around the idea of line detec-
               tor  units.   Mclelland  and  Rumelhart  (1986,  Ch.14)
               present a network similar in flavor,  although  with  a
               more  distributed representation.  Our network was ori-
               ginally designed to play a complete game by itself, and
               thus  has  considerable symmetry.  The control function
               included in the examples subdirectory allows  the  net-
               work  to  make  moves for just one player, going either
               first or second.

                    At the grossest level the network consists of  two
               halves  operating  in parallel, each computing the best
               move for one of the players.  Each half consists  of  a
               3x3  array  of  units  representing the board, an eight
               unit group representing the eight possible lines  on  a
               Tic-Tac-Toe  board, a 3x3 array representing the possi-
               ble moves, and a 3x3 array which is used  to  gate  the
               final  decision  back down to the board units.  Why are
               there two representations of the board?  One 3x3  array
               represents  the plays X has made, the other the plays O
               has made.

                    O's line units will be excited by O's board  units
               and  inhibited  by  X's  board units.  These units will
               then reflect the desirability for O of playing on  each
               line  -  the  more  places  O  has filled on a line the
               better a candidate it is for completion.  Similarly O's
               move  units  are  excited  by O's line units _a_n_d by X's
               line units - O wishes to play so  as  to  both  attempt
               completion on as many lines as possible for himself and
               to block completion on as many lines as possible for X,
               and  simultaneous  satisfaction  of  these goals is the
               best solution.  An exactly analagous  situation  exists
               for  X.   The  move units will be arranged in a winner-












               takes-all network which will settle on the most  active
               move.   The  gate  units will propogate the move to the
               appropriate board unit when the WTA network has relaxed
               to  a solution.  In addition squares which have already
               been filled by either player must be cut out in the WTA
               action.

                    So far we have an outline of the mechanism whereby
               the best move without lookahead will be determined.  In
               addition, for the full game playing ability, we need to
               alternate  moves.   This  we  achieve by sequencing the
               gating action.  We will have a control  unit  for  each
               half  of  the network.  One control unit will be "play-
               ing" at a time.  It will detect when  the  WTA  network
               has settled, and will activate the gate.  After a small
               delay, which we shall implement with a delay  unit,  to
               allow the effects of the move to propogate, the control
               unit will move into a "waiting" state and activate  the
               opponent's  control  unit,  which  will  move  into the
               "playing state".  Thus moves will alternate.

                    What will the links be?  Each board unit will have
               a  link to all the line units it affects, and a link to
               the corresponding move unit (this  to  affect  the  WTA
               action).   Each  line  unit will have a link to all the
               move units it affects.  The WTA network will be  imple-
               mented  by each move unit sending inhibition to all the
               others.  The control unit will have a  link  from  each
               move unit to enable it to detect when the WTA action is
               complete.  The gate units will have  a  link  from  the
               corresponding  move  units, and a link from the control
               which will cause the gate to open.  Each gate unit will
               have  a link to the corresponding board unit.  The con-
               trol and delay units will be arranged  in  a  loop:  X-
               control,  X-delay,  O-control,  O-delay,  around  which
               activation will flow.  In addition the control unit for
               each  half  will  have  a  link  to both arrays of move
               units, to reset them after a move has been made.

                    Now we need to specify the unit functions.   Board
               units  will have one site which will sum the input, two
               states, ON or OFF.  OFF will be the  start  state,  any
               input  causing a permanent transition to ON.  Potential
               and output will be 1 in the OFF state and 3 in  the  ON
               state.

                    The X line units will have two sites,  excite  and
               inhibit.   The  former  will  take  the  product of its
               inputs (from the X board units), thus producing  values
               of  1,  3,  9,  or  27 when there are zero, one, two or
               three X's respectively  on  the  line  which  the  unit
               represents.   The  latter will take the maximum of it's
               inputs (from the O board units), producing values of  1
               if  there  are  no O's on the line, and 3 if there are.


                                          1









               The unit function will set potential and output to 0 if
               the  inhibit  site value is greater than 1 (the line is
               occupied by at least one  O)  and  the  value  of  site
               excite  if  not.   An  analogous situation exists for O
               line units.

                    The  X  move  units  will   have   three   states,
               MOVE_PLAY,  MOVE_BLOCK,  and  MOVE_WAIT.  It will be in
               the first state when it is competing in the WTA network
               to  be  the  selected  move.   It will be in the second
               state when the square it represents  is  already  occu-
               pied.  It will be in the third state for a short period
               after a move has been made while the  effects  of  that
               move are propogating.  There will be five sites: excite
               sums up the inputs from the X line units; exciteb  sums
               up  the  inputs  greater  than  1 from the O line units
               (i.e. those lines with O's); inhibit will take the max-
               imum  of  the  inhibitions from the other move units in
               the WTA network; block will take  the  maximum  of  its
               inputs,  indicating  if the square is already occupied;
               reset will sum the positive  inputs  from  the  control
               units indicating if the unit should enter the MOVE_WAIT
               state.  The WTA action will be implemented by comparing
               the  incoming  inhibition with the incoming excitation.
               If the excitation is less,  the  potential  and  output
               will  be zero.  If the excitation is equal to the inhi-
               bition (i.e. the most active other unit is as active as
               this  one) then the unit will have potential and output
               set to either the excitation value or that  value  plus
               one,  randomly.   This  is  to  resolve conflicts where
               several moves are equally good.  If the  excitation  is
               greater  than  the  inhibition, then the unit will have
               potential and output equal to the excitation.  A  simi-
               lar situation exists for the O move units.

                    The gate units will have one site receiving  input
               from  the  control  unit and the appropriate move unit.
               It will only have output if  both  inputs  are  active,
               i.e. it is a conjunctive unit.

                    The control units will have two sites, excite  and
               delay.  The former will be activated by the move units,
               and will compute the exclusive or function (value 1  if
               and  only  if exactly one input is active).  The latter
               will receive input from the delay unit in  the  control
               loop,  signalling  the  control  unit  that  it is this
               player's  turn.   The  unit  will  have   two   states,
               CONTROL_WAIT  indicating it is the other player's turn,
               and CONTROL_PLAY indicating it is this player's turn.

               _1._1._2.  _P_r_o_g_r_a_m _D_e_s_i_g_n

                    It should be clear that the  network  has  a  high
               degree  of  structure, which we shall take advantage of


                                          2









               to modularize the program.  First consider  the  units.
               There  are  arrays of board, line, move and gate units.
               We shall have functions to make  these  arrays,  taking
               the  name  of  the  array  as a parameter.  In addition
               there will be a function to make the control and  delay
               units.

                    The links are also  highly  regular.   Those  that
               connect  corresponding units in the arrays will be made
               by a function.  The links from board to line units  and
               from  line  to move units will be made by two functions
               taking the names of the arrays and the destination site
               as parameters.  The Winner-Takes-All links will be made
               by a function taking the names of  the  array  and  the
               control unit as parameters.  The links from the control
               units to the move units will be made by a function tak-
               ing  the names of the units as parameters.  The control
               circuit loop will be made by another function.  Finally
               there  will  be  a  main network building function that
               allocates 100 units (more than enough)  and  calls  the
               functions above in the correct order.

               _1._1._3.  _P_r_o_g_r_a_m _C_o_d_e

                    First we show the preliminary code which  sets  up
               names for states, includes necessary definitions, etc.

                       #include <stdio.h>
                       #include "/u/connect/include/sim.h"             /* simulator functions */
                       #include "/u/connect/include/lib.h"             /* library functions */
                       #include <math.h>                               /* math functions */

                       #define max(r,s) (((r) > (s)) ? (r) : (s))      /* finds max of r and s */

                       #define CONTROL_WAIT 12                         /* unit states,potentials... */
                       #define CONTROL_PLAY 2

                       #define DELAY_WAIT 3
                       #define DELAY_PRIME 4

                       #define DELAY_FIRE 5

                       #define MOVE_BLOCK 6
                       #define MOVE_PLAY 7
                       #define MOVE_WAIT 8

                       #define XPLAYING 9
                       #define OPLAYING 10
                       #define NOWINNER 11
                       #define DRAW 13

                       #define ON 1
                       #define OFF 0
                       #define DATA 0


                                          3









                       #define BOARD_RESET_POT 1
                       #define BOARD_INIT_POT 1
                       #define BOARD_INIT_OUT 1

                       #define RESET_POT 0
                       #define INIT_POT 0
                       #define INIT_OUT 0

                       int  board_func  ();                            /* unit and site functions */
                       int  line_func   ();                            /* must be declared before */
                       int  move_func   ();                            /* they are used           */
                       int  delay_func  ();
                       int  control_func();
                       int  detect_func  ();
                       int  iotrigger_func ();
                       int  boardshow_func ();
                       int  moveexcite  ();
                       int  moveexciteb ();
                       int  movereset   ();

                       int TURN = 0;                                   /* user playing 1st or 2nd */


                       void MakeStateNames()                           /* declare state names */

                       {DeclareState("c_wait",CONTROL_WAIT);
                        DeclareState("c_play",CONTROL_PLAY);
                        DeclareState("d_wait",DELAY_WAIT);
                        DeclareState("d_prime",DELAY_PRIME);
                        DeclareState("d_fire",DELAY_FIRE);
                        DeclareState("m_block",MOVE_BLOCK);
                        DeclareState("m_play",MOVE_PLAY);
                        DeclareState("m_wait",MOVE_WAIT);
                        DeclareState("x to go",XPLAYING);
                        DeclareState("o to go",OPLAYING);
                        DeclareState("on",ON);
                        DeclareState("off",OFF);
                       }


                    All of the above should be clear if you have  read
               the  Four-Color  example.   The function MakeStateNames
               will be called to make the state  names  known  to  the
               simulator.

               _1._1._3._1.  _U_n_i_t _M_a_k_i_n_g _F_u_n_c_t_i_o_n_s

                    Below is the code to make a 9 unit (3x3) array  of
               board units.  Notice the name of the array is a parame-
               ter to the function.  Also  notice  that  the  name  is
               attached to the array when the first unit is created.

                       void MakeBoard(name)                            /* make board units */
                          char *name;                                  /* for X or O */


                                          4









                       {   int i,u;

                           for(i = 1; i < 10; i++)
                              {u = MakeUnit(   "board",
                                               board_func,
                                               BOARD_RESET_POT,
                                               BOARD_INIT_POT,
                                               DATA,
                                               BOARD_INIT_OUT,
                                               OFF,
                                               OFF);
                               if (i == 1) NameUnit(name,1,u,9);
                               AddSite(u,"excite",SFsum);
                              }
                       }


                    For the line units, we make 8 units  corresponding
               to  the eight lines.  The move and gate unit arrays are
               of size 9 (3x3).

                       void MakeLines(name)                            /* make line units */
                          char *name;                                  /* for X or O */

                       {   int i, u;

                           for(i = 1; i < 9; i++)
                              {u = MakeUnit(   "line",
                                               line_func,
                                               RESET_POT,
                                               INIT_POT,
                                               DATA,
                                               INIT_OUT,
                                               OFF,
                                               OFF);
                               if (i == 1) NameUnit(name,1,u,9);
                               AddSite(u,"excite",SFprod);
                               AddSite(u,"inhibit",SFmax);
                              }
                       }

                       void MakeMoves(name)                            /* make move units */
                          char *name;                                  /* for X or O */

                       {   int i, u;

                           for(i = 1; i < 10; i++)
                              {u = MakeUnit(   "move",
                                               move_func,
                                               RESET_POT,
                                               INIT_POT,
                                               DATA,
                                               INIT_OUT,
                                               MOVE_WAIT,


                                          5









                                               MOVE_WAIT);
                               if (i == 1) NameUnit(name,1,u,9);
                               AddSite(u,"excite",moveexcite);
                               AddSite(u,"exciteb",moveexciteb);
                               AddSite(u,"inhibit",SFmax);
                               AddSite(u,"block",SFmax);
                               AddSite(u,"reset",movereset);
                              }
                       }

                       void MakeGates(name)                            /* make gate units */
                          char *name;                                  /* for X or O */

                       {   int i, u;

                           for(i = 1; i < 10; i++)
                              {u = MakeUnit(   "gate",
                                               UFsum,
                                               RESET_POT,
                                               INIT_POT,
                                               DATA,
                                               INIT_OUT,
                                               OFF,
                                               OFF);
                               if (i == 1) NameUnit(name,1,u,9);
                               AddSite(u,"excite",SFand);
                              }
                       }


                    The function MakeControl  makes  the  control  and
               delay units which comprise the control loop:

                       void MakeControl()                              /* make control, delay and */
                                                                       /* win/draw detector units */
                       {   int u;

                           u = MakeUnit("delay",                       /* X delay unit */
                                        delay_func,
                                        RESET_POT,
                                        INIT_POT,
                                        DATA,
                                        INIT_OUT,
                                        DELAY_PRIME,
                                        DELAY_PRIME);
                           NameUnit("xdelay",0,u,1);
                           AddSite(u,"excite",SFsum);
                           u = MakeUnit("delay",                       /* O delay unit */
                                        delay_func,
                                        -1,
                                        -1,
                                        DATA,
                                        INIT_OUT,
                                        DELAY_WAIT,


                                          6









                                        DELAY_WAIT);
                           NameUnit("odelay",0,u,1);
                           AddSite(u,"excite",SFsum);
                           u = MakeUnit("control",                     /* X control unit */
                                        control_func,
                                        -1,
                                        -1,
                                        DATA,
                                        1,
                                        CONTROL_WAIT,
                                        CONTROL_WAIT);
                           NameUnit("xcontrol",0,u,1);
                           AddSite(u,"excite",SFxor);
                           AddSite(u,"inhibit",SFsum);
                           AddSite(u,"play",SFsum);
                           u = MakeUnit("control",                     /* O control unit */
                                        control_func,
                                        -1,
                                        -1,
                                        DATA,
                                        INIT_OUT,
                                        CONTROL_WAIT,
                                        CONTROL_WAIT);
                           NameUnit("ocontrol",0,u,1);
                           AddSite(u,"excite",SFxor);
                           AddSite(u,"inhibit",SFsum);
                           AddSite(u,"play",SFsum);
                       }


               _1._1._3._2.  _L_i_n_k _M_a_k_i_n_g _F_u_n_c_t_i_o_n_s

                    The links from board to line and from line to move
               units  are  a  little  complex,  but  there is a lot of
               structure.  Visualizing a tic-tac-toe board, we  number
               consistently from left to right and top to bottom using
               indices 0 to 8.  Thus cell  0  is  the  top  row,  left
               column,  cell 3 the middle row, left column, cell 6 the
               bottom row, middle column, etc.   Similarly  we  number
               the  lines 0 to 7 with 0, 1, 2 representing top,middle,
               bottom rows; 3, 4, 5 representing left,  middle,  right
               columns; 6 top left to bottom right diagonal; and 7 top
               right to bottom left diagonal.   Then  the  code  below
               will  create  the  correct  links.  You will notice the
               similarity in the two functions.  This is because if  a
               cell votes for a line, then the line votes for the same
               cell.

                       void BoardToLine(from,to,site)                  /* links from board to */
                           char *from,*to,*site;                       /* line units, for X or O */

                       {   int i;

                           for (i = 0; i < 9; i++)


                                          7









                              {int u;
                               u = NameToInd(from,i);
                               MakeLink(u,NameToInd(to,i / 3),site,1,0,NULL);     /* lines 0,1,2 */
                               MakeLink(u,NameToInd(to,3 + i % 3),site,1,0,NULL); /* lines 3,4,5 */
                               if (i % 4 == 0)
                                  MakeLink(u,NameToInd(to,6),site,1,0,NULL);      /* line 6 */
                               if ((i % 8 != 0) && (i % 2 == 0))
                                  MakeLink(u,NameToInd(to,7),site,1,0,NULL);      /* line 7 */
                              }
                       }

                       void LineToMove(from,to,site)                   /* links from line to move */
                           char *from,*to,*site;                       /* units, for X or O */

                       {   int i;

                           for (i = 0; i < 9; i++)
                              {int u;
                               u = NameToInd(to,i);
                               MakeLink(NameToInd(from,i / 3),u,site,1,0,NULL);    /* lines 0,1,2 */
                               MakeLink(NameToInd(from,3 + i % 3),u,site,1,0,NULL);/* lines 3,4,5 */
                               if (i % 4 == 0)
                                  MakeLink(NameToInd(from,6),u,site,1,0,NULL);     /*line 6 */
                               if ((i % 8 != 0) && (i % 2 == 0))
                                  MakeLink(NameToInd(from,7),u,site,1,0,NULL);     /* line 7 */
                              }
                       }


                    The winner-takes-all network is made by construct-
               ing  links  from  each unit to each of the other units'
               inhibit site and to the control unit.

                       void WTA(name,control)                          /* make a vector of 9 units */
                           char *name,*control;                        /* into a winner takes all */
                                                                       /* network, with controller */
                       {   int i,j,u,c;

                           for (i = 0; i < 9; i++)
                             {u = NameToInd(name,i);
                              c = NameToInd(control,0);
                               for (j = 0; j < 9; j++)
                                 if (i != j)
                                   MakeLink(u,NameToInd(name,j),"inhibit",1,0,NULL);
                               MakeLink(u,c,"excite",1,0,NULL);
                              }
                       }


                    The links between corresponding  elements  of  the
               arrays  is  simple.   The board blocks moves, the moves
               excite the gates, the gates excite the board,  and  the
               control units enable the gates.



                                          8









                       void ArrayMatches()                             /* matching 3x3 arrays of */
                                                                       /* units are linked */
                       {   int i,bx,bo,mx,mo,gx,go,xc,oc;

                           bx = NameToInd("xboard",0);
                           bo = NameToInd("oboard",0);
                           mx = NameToInd("xmove",0);
                           mo = NameToInd("omove",0);
                           gx = NameToInd("xgate",0);
                           go = NameToInd("ogate",0);
                           xc = NameToInd("xcontrol",0);
                           oc = NameToInd("ocontrol",0);
                           for (i = 0; i < 9; i++)
                               MakeLink(bx,mx+i,"block",1,0,NULL);
                               MakeLink(bx,mo+i,"block",1,0,NULL);
                               MakeLink(bo,mo+i,"block",1,0,NULL);
                               MakeLink(bo,mx+i,"block",1,0,NULL);
                               MakeLink(mo,go+i,"excite",1,0,NULL);
                               MakeLink(mx,gx+i,"excite",1,0,NULL);
                               MakeLink(gx,bx+i,"excite",1,0,NULL);
                               MakeLink(go,bo+i,"excite",1,0,NULL);
                               MakeLink(xc,gx+i,"excite",1,0,NULL);
                               MakeLink(oc,go+i,"excite",1,0,NULL);
                              }
                       }


                    This function links a control unit  to  the  reset
               sites of a move array:

                       void ControlToMove(control,move)                /* links control unit to */
                           char *control,*move;                        /* array of move units */

                       {   int c,i,u;

                           c = NameToInd(control,0);
                           for (i = 0; i < 9; i++)
                              {u = NameToInd(move,i);
                               MakeLink(c,u,"reset",1,0,NULL);
                              }
                       }


                    This  function  sets  up  the  control  loop  that
               ensures sequential play.

                       void ControlCircuit()                           /* links to ensure two */
                                                                       /* halves play sequentially */
                       {   int xc,oc,xd,od;

                           xc = NameToInd("xcontrol",0);
                           oc = NameToInd("ocontrol",0);
                           xd = NameToInd("xdelay",0);
                           od = NameToInd("odelay",0);


                                          9









                           MakeLink(xc,xc,"inhibit",1,0,NULL);
                           MakeLink(oc,oc,"inhibit",1,0,NULL);
                           MakeLink(xc,xd,"excite",1,0,NULL);
                           MakeLink(oc,od,"excite",1,0,NULL);
                           MakeLink(xd,oc,"play",1,0,NULL);
                           MakeLink(od,xc,"play",1,0,NULL);
                       }


               _1._1._3._3.  _M_a_i_n _N_e_t_w_o_r_k _B_u_i_l_d_i_n_g _F_u_n_c_t_i_o_n

                    The function build below is called from the  simu-
               lator to construct the network.  It allocates the units
               and then calls the functions described  above  to  make
               the units and the links.

                       void build()                                    /* master network building */
                                                                       /* routine */
                       {
                           AllocateUnits(100);                         /* actually use 75 */

                           /*Now make the units*/

                           MakeBoard("xboard");                        /* make units for board, */
                           MakeBoard("oboard");                        /* lines, moves and gates */
                           MakeLines("xline");
                           MakeLines("oline");
                           MakeMoves("xmove");
                           MakeMoves("omove");
                           MakeGates("xgate");
                           MakeGates("ogate");
                           MakeControl();                              /* make control units */

                           /*Now make the links*/

                           BoardToLine("xboard","xline","excite");     /* link boards to lines */
                           BoardToLine("oboard","oline","excite");
                           BoardToLine("xboard","oline","inhibit");
                           BoardToLine("oboard","xline","inhibit");

                           LineToMove("xline","xmove","excite");       /* link lines to moves */
                           LineToMove("oline","omove","excite");
                           LineToMove("xline","omove","exciteb");
                           LineToMove("oline","xmove","exciteb");

                           WTA("xmove","xcontrol");                    /* move units are formed */
                           WTA("omove","ocontrol");                    /* into winner-takes-all */

                           ArrayMatches();                             /* link matching arrays */
                           ControlToMove("xcontrol","omove");          /* feedback links from */
                           ControlToMove("ocontrol","xmove");          /* control units to move */
                           ControlToMove("xcontrol","xmove");          /* units, both X and O */
                           ControlToMove("ocontrol","omove");
                           ControlCircuit();                           /* sequence players */


                                         10









                           /*Now make the state names*/

                           MakeStateNames();                           /* declare the state names */

                       }


               _1._1._3._4.  _U_n_i_t, _S_i_t_e _a_n_d _L_i_n_k _F_u_n_c_t_i_o_n_s

                    The unit function for the board units follows.  It
               checks  the  value at the site, and sets the output and
               potential to 3 if it is greater than zero, otherwise 1.

                        board_func (up)
                          Unit *up;

                               /* board_func returns 3 if it is excited, 1 otherwise */

                       {  Site *sp;
                          int tot = 0;

                          if (SiteValue("excite",up->sites) > 0)
                               up->state = ON;                         /* any excitement turns on! */
                          up->output = up->potential = (up->state == ON) ? 3 : 1;
                       }


                    The line units sets output and potential to  0  if
               it is inhibited, otherwise the amount of excitation.

                        line_func (up)
                          Unit *up;

                               /* line_func returns 0 if unit is inhibited, the excited value else*/

                        { if (SiteValue("inhibit",up->sites) > 1)
                             up->potential = up->output = 0;
                          else
                             up->potential = up->output = SiteValue("excite",up->sites);
                        }


                    The move units have the most  complex  unit  func-
               tion, reflecting their three possible states.  In state
               MOVE_PLAY, if site block is active the unit switches to
               state  MOVE_BLOCK,  or  if  site  reset  is  active  it
               switches to state  MOVE_WAIT.   Otherwise  it  compares
               excitation  and  inhibition for the WTA action and sets
               output and potential accordingly.  If the  unit  is  in
               state  MOVE_BLOCK, it stays there (square already occu-
               pied).  When the unit is put into  state  MOVE_WAIT  it
               counts  six  time  steps  and  then switches into state
               MOVE_PLAY.



                                         11









                       move_func (up)
                          Unit *up;

                               /* move_func sets the potential to be the difference between
                                  excites and inhibits, unless block is active
                               */

                       {switch(up->state)
                          {case MOVE_PLAY: if (SiteValue ("block",up->sites) > 2) /*already occupied*/
                                             {up->state = MOVE_BLOCK;
                                              up->potential = 0;
                                             }
                                           else                        /* not occupied */
                                             if (SiteValue ("reset",up->sites) > 0)
                                               {up->state = MOVE_WAIT; /*reset for next*/
                                                up->potential = 0;
                                               }
                                             else                      /* winner-takes-all */
                                               {int myexc,othexc,pot,inh;

                                                myexc = SiteValue("excite",up->sites);
                                                                       /* good offensive moves */
                                                othexc = SiteValue("exciteb",up->sites);
                                                                       /* good blocking moves */
                                                inh = SiteValue("inhibit",up->sites);
                                                                       /* maximum of competitors */
                                                pot = up->potential;   /* previous activation */
                                                myexc += max(0,othexc-1);
                                                                       /* can't be negative */
                                                myexc = max(myexc,pot);
                                                                       /* not less than last time */
                                                if (myexc == inh)      /* if two equal winners */
                                                  myexc = (((random() %128) / 128.0) > 0.5) ?
                                                    (1+myexc) : myexc; /* randomly add 1 to potent. */
                                                else                   /* definite winner or loser */
                                                  myexc = (myexc > inh) ? myexc : 0;
                                                                       /* set to zero if loser */
                                                up->potential = myexc;
                                               }
                                           up->output = up->potential;
                                           break;

                           case MOVE_BLOCK: up->potential = 0;
                                            up->output = 0;
                                            break;

                           case MOVE_WAIT: if (up->potential < 6)      /* wait for effects of */
                                             up->potential++;          /* previous move to propogate*/
                                           else                        /* waited long enough */
                                             {up->state = MOVE_PLAY;
                                              up->potential = 0;
                                             }
                                           up->output = 0;
                                           break;


                                         12









                          }
                       }


                    The  control  unit   can   be   in   two   states,
               CONTROL_WAIT  and CONTROL_PLAY.  In the former it waits
               for a signal at site play and then  switches  into  the
               latter,  in  which  it waits until the WTA settling has
               occured.  Then it sends an output signal  and  switches
               back into state CONTROL_WAIT

                        control_func (up)                              /* determines when WTA */
                          Unit *up;                                    /* network has settled */

                       {switch (up->state)
                          {case CONTROL_WAIT:                          /* other players turn */
                            if (SiteValue ("play",up->sites) != 0)     /* now my turn */
                              up->state = CONTROL_PLAY;
                            up->potential = 0;
                            break;

                           case CONTROL_PLAY:                          /* waiting for settling */
                            if (SiteValue ("excite",up->sites) != 0)   /* if only one move active */
                              {up->state = CONTROL_WAIT;
                               up->potential = 1;                      /* send signal */
                              }
                            else
                              up->potential = 0;
                          }
                        up->output = up->potential;
                       }


                    The delay units can be waiting to be signalled  by
               the control unit preceding them in the loop, or primed,
               in which state they remain for 6 time steps, or  firing
               to  signal  the  control  unit  next  in the loop.  The
               sequence of states is wait, prime, fire,  wait,  prime,
               fire,....

                        delay_func (up)                                /* wait after move for */
                          Unit *up;                                    /* effects to propogate */
                                                                       /* before signalling other */
                       {switch (up->state)
                          {
                          case DELAY_WAIT:                             /* waiting for signal */
                            if (SiteValue ("excite",up->sites) > 0)
                              up->state = DELAY_PRIME;
                            up->output = up->potential = 0;
                            break;

                          case DELAY_PRIME:                            /* delay 6 timesteps */
                            if (up->potential > 5)
                              up->state = DELAY_FIRE;


                                         13









                            else
                              up->potential++;
                            up->output = 0;
                            break;

                          case DELAY_FIRE:                             /* signal other player */
                            up->state = DELAY_WAIT;
                            up->potential = 0;
                            up->output = 1;
                            break;
                          }
                       }


                    Most of the site functions used in the network are
               standard library functions.  However a few special pur-
               pose ones are necessary.  moveexcite is the function at
               the  site  receiving  inputs  from friendly line units.
               moveexciteb is function at the site receiving  opponent
               line  units.  movereset receives input from the control
               unit.

                       /* moveexcite returns the sum of inputs, except that any
                          input of 9, indicating 2 tokens in row, is doubled
                       */

                        moveexcite (up,sp)
                          Unit *up;
                          Site *sp;

                          {Link *ip;
                           int i,j, tot = 0;

                           for (ip = sp->inputs; ip != NULL; ip = ip->next)
                               {j = *(ip->value);
                                if (j == 9)
                                   tot += (j + j);
                                else
                                   tot += j;
                               }
                           sp->value = tot;
                          }

                       /* moveexciteb returns the sum of the inputs, ignoring inputs of 1 */

                        moveexciteb (up,sp)
                          Unit *up;
                          Site *sp;

                          { int tot = 0;
                            int temp;
                            Link *ip;

                           for (ip = sp->inputs; ip != NULL; ip = ip->next)


                                         14









                            {temp = *(ip->value);
                             if (temp > 1) tot += temp;
                            }
                           sp->value = tot;
                          }

                       /* movereset returns the sum of the positive inputs */

                        movereset (up,sp)
                          Unit *up;
                          Site *sp;

                       {   int tot = 0;
                           int i, j;
                           Link *ip;

                           for(ip = sp->inputs; ip != NULL; ip = ip->next)
                            { j =  *(ip->value);
                              if (j > 0) tot += j;
                            }
                           sp->value = tot;

                       }


                    The link function in all cases is the  NULL  func-
               tion.   To make simulation faster the NO_LINK_FUNC_FLAG
               should be set for all sites, but since this is a  small
               network (approx 76 units, several hundred links), there
               is no noticeable delay.

               _1._1._4.  _M_o_d_u_l_a_r_i_t_y

                    This network is a good illustration of the princi-
               ple  of  modularity.   By  breaking the network up into
               parts of great regularity,  it  is  possible  to  write
               functions  to  build  it  piece by piece.  This reduces
               considerably the size of the program,  as  compared  to
               explicitly  making  each  link  and  unit.  One must be
               careful that  all  pieces  have  been  constructed  and
               correctly linked however.

               _1._1._5.  _I_n_p_u_t _a_n_d _O_u_t_p_u_t

                    To make this tic-tac-toe player a useable game, we
               need  to  be able to display the state of the board and
               to get the user's moves, if any.  To do this we  intro-
               duce  three  more units.  The trigger unit is activated
               by both control units, i.e. it detects when the network
               is  making  a move.  It in turn activates the show unit
               which has a very complex unit function.  This  function
               is  way  outside the connectionist framework.  It exam-
               ines the internals of the simulator  to  find  out  the
               state  of  the board, displays the board on the screen,


                                         15









               and asks the user to specify a move in  response.   The
               third unit is the detector units which detects when one
               player has won or the board is filled.  For this it has
               two  sites,  one  with  inputs  from  the line units to
               detect a win (activation 27) and one with  inputs  from
               the  board  units  to  detect the draw (activation 36).
               The show unit has code to adjust the potential and out-
               put  of several units to force the network to act as if
               it has just played (really the user has  specified  the
               move).   The  detector unit asks the user if she wishes
               to play again, and if so resets the network to its ini-
               tial  state  and  calls  the  simulator  for  120 steps
               (enough for a game).

                    This kind of code is not  recommended  unless  you
               know  what  you  are  doing.  It is highly _n_o_n-_s_t_a_n_d_a_r_d
               code; if you are going to change the internals  of  the
               simulator  in  the  way  this  code does, you risk bugs
               which are very hard to trace.  The User Interface  code
               appears  below.   To  attach  the units to the network,
               _M_a_k_e_I_O and _U_n_i_t_s_T_o_D_e_t_e_c_t must be called from  the  main
               build function.

                       void MakeIO()                                   /* make inpuy/output units */
                                                                       /* and links */
                       {   int i,j,k,iotrig,brdsh;

                           u = MakeUnit("detect",                      /* win/draw detector unit */
                                        detect_func,
                                        RESET_POT,
                                        INIT_POT,
                                        DATA,
                                        INIT_OUT,
                                        OFF,
                                        OFF);
                           NameUnit("detector",0,u,1);
                           AddSite(u,"xlines",SFmax);
                           AddSite(u,"olines",SFmax);
                           AddSite(u,"occupied",SFsum);
                           iotrig = MakeUnit("delay",                  /* trigger board display */
                                             iotrigger_func,
                                             2,
                                             2,
                                             DATA,
                                             INIT_OUT,
                                             OPLAYING,
                                             OPLAYING);
                           NameUnit("iotrigger",0,iotrig,1);
                           brdsh = MakeUnit("ioshow",                  /* display board */
                                            boardshow_func,
                                            RESET_POT,
                                            INIT_POT,
                                            DATA,
                                            INIT_OUT,


                                         16









                                            OFF,
                                            OFF);
                           NameUnit("boardshow",0,brdsh,1);
                           AddSite(iotrig,"xplay",SFsum);
                           AddSite(iotrig,"oplay",SFsum);
                           MakeLink(NameToInd("xcontrol",0),iotrig,"xplay",1,0,NULL);
                           MakeLink(NameToInd("ocontrol",0),iotrig,"oplay",1,0,NULL);
                                                               /* display is triggered by a move */
                           AddSite(brdsh,"show",SFsum);
                           MakeLink(iotrig,brdsh,"show",1,0,NULL);
                       }

                       void UnitsToDetect()                            /* link line and board units */
                                                                       /* to detector for win/draw */
                       {   int xl,ol,det,i;

                           xl = NameToInd("xline",0);
                           ol = NameToInd("oline",0);
                           det = NameToInd("detector",0);
                           for (i = 0; i < 8; i++)
                             {MakeLink(xl+i,det,"xlines",1,0,NULL);    /* line units indicate a win */
                              MakeLink(ol+i,det,"olines",1,0,NULL);
                             }
                           xl = NameToInd("xboard",0);
                           ol = NameToInd("oboard",0);
                           det = NameToInd("detector",0);
                           for (i = 0; i < 9; i++)                     /* board units indicated a */
                             {MakeLink(xl+i,det,"occupied",1,0,NULL);  /* draw - if 9 are on */
                              MakeLink(ol+i,det,"occupied",1,0,NULL);
                             }
                       }

                       showboard ()                                    /* display the board */

                                   { /*show board*/
                                       int i, xb, ob, maxopt = 0;
                                       int brd[9],opt[9],state;
                                       char user[30];

                                       xb = NameToInd("xboard",0);
                                       ob = NameToInd("oboard",0);
                                       printf ("Board is now:0);
                                       for (i = 0; i < 9; i++)         /* check each square */
                                          {if (i % 3 == 0) printf("0-----0");
                                           if (UnitList[xb+i].state == ON)
                                               {brd[i] = -1;
                                                printf("X|");          /* occupied by X */
                                               }
                                           else
                                               if (UnitList[ob+i].state == ON)
                                                  {printf("O|");
                                                   brd[i] = -1;        /* occupied by O */
                                                  }
                                               else


                                         17









                                                  {printf(" |");       /* not occupied */
                                                   brd[i] = maxopt;
                                                   opt[maxopt] = i;
                                                   maxopt += 1;
                                                  }
                                         }
                                       printf("0-----0);
                                     }

                       detect_func (up)                                /* detect win or draw */
                          Unit *up;

                       {int winner;

                        if (SiteValue ("xlines",up->sites) > 26)       /* 3 in a row give 27 */
                          {winner = XPLAYING;
                           showboard();
                           printf ("X has won!... Do you want another game ?0);
                         }
                        else
                          if (SiteValue ("olines",up->sites) > 26)
                            {winner = OPLAYING;
                             showboard();
                             printf ("O has won!... Do you want another game ?0);
                           }
                          else
                            if (SiteValue ("occupied",up->sites) > 35) /* all occupied give 36 */
                              {winner = DRAW;
                               showboard();
                               printf ("The game is a draw!... Do you want to play again ?0);
                              }
                            else
                              winner = NOWINNER;                       /* game unfinished */
                        if (winner != NOWINNER)
                          {char user[30];                              /* ask user if wants to */
                           user[0] = 'x';                              /* play again */
                           while ((user[0] != 'n') && (user[0] != 'y'))
                             {printf("[yes,no] > ");
                              scanf("%s",user);
                             }
                           if (user[0] == 'y')                         /* another game, so... */
                             {Reset();                                 /* reset the network */
                              TURN = 0;                                /* reset who goes first */
                              Step(120);                               /* run for another 120 steps */
                             }
                           else                                        /* no more games, so... */
                             exit(0);                                  /* exit to UNIX shell */
                          }
                       }

                       iotrigger_func(up)                              /* fire when a move is made */
                          Unit *up;

                       {  up->output = 0;


                                         18









                          if (up->potential == 0)
                            if ((SiteValue("xplay",up->sites) > 0) ||
                                (SiteValue("oplay",up->sites) > 0))
                                   up->potential = 1; else ;
                          else
                            if (up->potential > 1)
                               {up->potential = 0;
                                up->output = 1;
                               }
                            else
                               up->potential += 1;
                           if (SiteValue("xplay",up->sites) > 0)
                               up->state = OPLAYING;
                           if (SiteValue("oplay",up->sites) > 0)
                               up->state = XPLAYING;
                       }

                       boardshow_func(up)                              /* display board and if it*/
                          Unit *up;                                    /* is the users turn then */
                                                                       /* request a move */
                       {  up->output = up->potential = 0;
                          if (up != NULL)
                               if (up->sites->value != 0)
                                 { /*show board*/
                                   int i, xb, ob, maxopt = 0;
                                   int brd[9],opt[9],state;
                                   char user[30];

                                   xb = NameToInd("xboard",0);         /* code as in showboard */
                                   ob = NameToInd("oboard",0);
                                   printf ("Board is now:0);
                                   for (i = 0; i < 9; i++)
                                     {if (i % 3 == 0) printf("0-----0");
                                      if (UnitList[xb+i].state == ON)
                                        {brd[i] = -1;
                                         printf("X|");
                                       }
                                      else
                                        if (UnitList[ob+i].state == ON)
                                          {printf("O|");
                                           brd[i] = -1;
                                         }
                                        else
                                          {printf(" |");
                                           brd[i] = maxopt;
                                           opt[maxopt] = i;
                                           maxopt += 1;
                                         }
                                    }
                                   printf("0-----0);
                                   state = UnitList[NameToInd("iotrigger",0)].state;
                                   if (state == XPLAYING)
                                     printf("with X to play0);
                                   else


                                         19









                                     printf("with O to play0);
                                   if (TURN == 0)                      /* first move to make */
                                     {printf("Do you wish to play first or second ?0);
                                      user[0] = 'x';
                                      while ((user[0] != '1') && (user[0] != '2'))
                                        {printf("[1 or 2] > ");
                                         scanf("%s",user);
                                       }
                                      if (user[0] == '1')
                                        TURN = 1;                      /* user playing first */
                                      else
                                        TURN = 2;                      /* user playing second */
                                    }
                                   if (((state == OPLAYING) && (TURN == 1)) ||
                                       ((state == XPLAYING) && (TURN == 2)))
                                                                       /* users turn to play */
                                     {int usq,uact,uslp;               /* show unoccupied squares */
                                      printf("Your turn... here are the choices...0);
                                      for (i = 0; i < 9; i++)
                                        {if (i % 3 == 0) printf("0-----0");
                                         if (brd[i] < 0)
                                           printf(" |");
                                         else
                                           printf("%d|",brd[i]);
                                       }
                                      printf("0-----0);
                                      printf("Choose an option0);
                                      i = -1;
                                      while ((i < 0) || (i > maxopt-1))
                                        {printf("[0..%d] > ",maxopt-1);
                                         scanf("%s",user);             /* get users move */
                                         i = user[0] - '0';
                                       }
                                      i = opt[i];
                                      if (state == XPLAYING)           /* find units to adjust */
                                        {usq = NameToInd("xboard",i);
                                         uact = NameToInd("odelay",0);
                                         uslp = NameToInd("xcontrol",0);
                                       }
                                      else
                                        {usq = NameToInd("oboard",i);
                                         uact = NameToInd("xdelay",0);
                                         uslp = NameToInd("ocontrol",0);
                                       }
                                      UnitList[usq].potential = 1;     /* change network features */
                                      UnitList[usq].output = 3;        /* to reflect move made */
                                      UnitList[usq].state = ON;
                                      UnitList[uact].potential = 0;
                                      UnitList[uact].state = DELAY_WAIT;
                                      UnitList[uslp].output = 1;
                                      UnitList[uslp].state = CONTROL_WAIT;
                                    }
                                 }
                               else ;


                                         20









                          else
                            printf("Error in proc boardshow_func0);
                        }

               _R_e_f_e_r_e_n_c_e_s

               Rumelhart, D. E. and J. L. McClelland, _P_a_r_a_l_l_e_l _D_i_s_t_r_i_-
                  _b_u_t_e_d  _P_r_o_c_e_s_s_i_n_g:  _E_x_p_l_o_r_i_n_g  _t_h_e _M_i_c_r_o_s_t_r_u_c_t_u_r_e _o_f
                  _C_o_g_n_i_t_i_o_n,  Cambridge,  Mass:   Bradford   Books/MIT
                  Press, 1986.














































                                         21



