Noughts and Crosses (aka Tic-Tac-Toe) Write a Notes and Crosses application.

User interface is optional.
You can output the computers move in text: eg 1x1 (meaning center square)

The computer must play an intelligent (extra marks for optimal) game.
Apart from its first move which should be random.

Player computer take turns at first move.
First player to win two games in a row wins.

Score:


Lowest strokes wins.
If equal best computer move algorithm (as I see fit)
If equal submitted date.
c99  -- 1229 characters (and needs nucrses)

#include &lt;ncurses.h&gt;
#include &lt;string.h&gt;
#define R return
#define A char
#define M mvprintw
#define I int
#define O "%c wins a game, press any key to %s."
A*s=" XOCHT";FILE *r;
I W(A*b){I i,p,q,r,w[]={0,1,2,0,3,6,0,2,3,3,3,1,1,1,4,2};if(!memchr(b,0,9))
R 3;for(i=0;i&lt;16;i++){p=w[i/2];q=w[i/2+8];r=i%2+1;if((b[p]==r)&amp;&amp;(b[p+q]==r)
&amp;&amp;(b[p+q+q]==r))R r;}R 0;};
I C(A*b, I s){A c[9];for(I i=0;i&lt;9;i++){memcpy(c,b,9);if(c[i]==0)c[i]=s;
if(s==W(c))R i;}R -1;};I P(A*b){I i;while(b[i=fgetc(r)%9]);R i;};
I D(A*b){A*p=" |-+";I i=0,j;for(;i&lt;5;i++)for(j=0;j&lt;5;j++)
mvaddch(i,j,p[2*(i%2)+j%2]);for(i=0;i&lt;9;i++)mvaddch(2*(i/3),2*(i%3),
b[i]?s[b[i]]:'1'+i);refresh();};
I G(A m){I u=1,i,j;A b[9];memset(b,0,9);M(1,9,"Computer %c",s[m]);M(2,9,
"Human %c",s[m%2+1]);do{M(4,9,"Up %c",s[u]);D(b);if(u==m){if((i=C(b,m))&lt;0&amp;&amp;(i=C
(b,m%2+1))&lt;0)i=P(b);b[i]=m;}else{do{M(6,0,"your move ");refresh();scanw("%d",&amp;i
);}while(i&gt;10||i&lt;1||b[i-1]!=0);b[i-1]=m%2+1;};u=u%2+1;}while(0==W(b));M(8,0,O,
s[W(b)],"continue");D(b);getch();M(8,0,"%44c",*s);R W(b)==3?5:u==m?4:3;}
I main(I c,A**v){A d=0, m=v[1][0]-'x'?2:1;r=fopen("/dev/random","r");
initscr();for(;;){c=G(m);if(c==d&amp;&amp;c-5)break;else M(4,25,"Previous winner %c",
s[c]),d=c;m=m%2+1;}M(9,0,O,c,"quit");getch();endwin();}


It gives away characters for the users convenience by using curses and having actual English prompts and informative lines. The obvious macro golfs have been made, but there are probably some comma and conditional operator improvements still available.

Is it smart? Well, it takes one move wins and blocks one move losses, but is otherwise quite random. (I may take the weekend to repair my interrupted CS education and learn about A* trees...) The "random first move" condition is met automatically as no one has the option to win at that point. Despite this obviously deficient AI I have yet to beat it when it holds X, so I have not defeated it by the "two in a row" rules.

Structurally we have a "is there a winner" function and a "can I win with a single play" function which works by coping the board and trying each position in turn with the isWon function. The single canWin function serves first to search for winning opportunities (by passing the computer's symbol) and then to search for things than need blocking (by passing the human's).

It declares Computer, Human, or Tie the winner on each game and only C or H for the series.

Now respects the spec as to winning conditions And plays well enough that you have to let it win or Ctrl-C to get out.

The version below differs from the above in prompts as well as macroization, but still represents the approach.

#include &lt;ncurses.h&gt;
#include &lt;string.h&gt;

char*s/*ymbols*/=" XOCHT";
FILE *r/*dev/random*/;

/* return the index of the side that wins, 0 if un-won or 3 if cat's game */
int W/*on*/(char*b){
  int i,p,q,r,w/*inning matrix*/[]={0,1,2,0,3,6,0,2,
                    3,3,3,1,1,1,4,2};
  if(!memchr(b,0,9))return 3; /* detect cat games by lack of open positions */
  for(i=0;i&lt;16;i++){
    p=w[i/2];   /* starting position of the winning sequence to check */
    q=w[i/2+8]; /* step size of the winning sequence to check */
    r=i%2+1;  /* winning value to check */
    if( (b[p]==r) &amp;&amp;
    (b[p+q]==r) &amp;&amp;
    (b[p+q+q]==r) )
      return r;
  }
  return 0;
};
/* returns the first square in which the current side can win or -1 if none*/
int C/*an win?*/(char*b, int s/*ide to play*/){
  char c[9];
  for(int i=0;i&lt;9;i++){
    memcpy(c,b,9);
    if(c[i]==0)c[i]=s;
    if(s==W(c))return i;
  }
  return -1;
};
/* returns a random unoccupied square */
int P/*ick a random move*/(char*b){
  /* assumes a move is possible and will loop forever if this
     condition is violated */
  int i;
  while(b[i=fgetc(r)%9])
    ;
  return i;
};
int D/*raw the board*/(char*b){
  char*p=" |-+";
  int i=0,j;
  for(;i&lt;5;i++)
    for(j=0;j&lt;5;j++)
      mvaddch(i,j,p[2*(i%2)+j%2]);
  for(i=0;i&lt;9;i++)
    mvaddch(2*(i/3),2*(i%3),b[i]?s[b[i]]:'1'+i);
  refresh();
};

/* Play a single game with the computer taking s[m] and return the winner
 *
 *  3 -- computer
 *  4 -- human
 *  5 -- tie
 */
int G(char m){
  int u/*p*/=1,i,j;
  char b/*oard*/[9]; 
  memset(b,0,9); /* initalized to 0 (empty) */
  mvprintw(1,9,"Computer uses %c",s[m]);
  mvprintw(2,9,"Human uses %c",s[m%2+1]);
  /* main loop */
  do {
    mvprintw(4,9,"To play %c",s[u]);
    D(b);
    if(u==m){
      // my turn to move
      if( (i=C(b,m))&lt;0 &amp;&amp;    /* move to win */
      (i=C(b,m%2+1))&lt;0 ) /* 2%2+1 == 1,  1%2+1 == 2 so, the other side */
    i=P(b);
      b[i]=m;
    } else {
      // user's turn
      /* get user selection */
      do {
    mvprintw(6,0,"your move:\n");
    refresh();
    scanw("%d",&amp;i);
      } while (i&gt;10||i&lt;1||b[i-1]!=0);
      b[i-1]=m%2+1;
    };
    u=u%2+1; /* Here u is switched to the *next* player, so if the
        game is one u denotes the *loser*  */
  } while(0==W(b));
  /* Print out the winner */
  mvprintw(8,0,"%c wins a game, press any key to continue.",s[W(b)]);
  D(b);
  getch();
  mvprintw(8,0,"%44c",*s);
  return W(b)==3?5:u==m?4:3; 
}

/* first argument is required and expected to be x or o 
 *
 * x always goes first
 */
int main(int c,char**v){
  char d=0, /* previous winner, starts on an impossible value */
    m=v[1][0]-'x'?2:1; /* select my symbol by index into s */
  /* /dev/random intialization */
  r=fopen("/dev/random","r");
  /* curses intialization */
  initscr(); /* start curses */
  for(;;){
    c=G(m);
    if (c==d&amp;&amp;c-5) // same *player* wins two in a row
      break; 
    else 
      mvprintw(4,25,"Previous winner %c",s[c]),d=c;
    m=m%2+1;
  }
  mvprintw(9,0,"Final winner is %c, press any key to quit.",c);
  getch();
  endwin();
}