############################################################################
#
#	File:     ttt.icn
#
#	Subject:  Program to play tic-tac-toe
#
#	Author:   Chris Tenaglia
#
#	Date:     August 14, 1996
#
############################################################################
#
#   This file is in the public domain.
#
############################################################################
#
#  This program plays the game of tic-tac-toe.
#
############################################################################
#
#  Links:  random
#
############################################################################

link random

global me,you,true,false,draw,pointer,wins,pass,taken,winner
global mark,row,routes,route

procedure main()
  local again, index, path, play, square, tmp, victory, your_last_move

  init()
  play := true
  while play == true do
    {
    me      := set()      # computer is me
    you     := set()      # player   is you
    victory := ""         # nobodys' won yet
    winner  := ""         # winner flag
    pass    := 0          # start flag
    taken   := table(false)    # taken position table (rather than set?)
    display()
#
# computer makes first move
#
    insert(me,1)
    taken[1] := true
    display()
#
# player follows
#
    insert(you,(tmp := integer(get_your_move())))
    taken[integer(tmp)] := true
    display()
    path := routes[tmp]              # players' move determines strategy
    index := 2                       # points at 2nd move just happened

#
# computers' next move determined from strategy list
#
    insert(me,(tmp := integer(path[(index+:=1)])))
    taken[tmp] := true
    display()
#
# player follows
#
    insert(you,(tmp := integer(get_your_move())))
    taken[integer(tmp)] := true
    your_last_move := tmp
    display()
#
# if didn't take position dictated, loss ensues
#
    if your_last_move ~= (tmp := integer(path[(index+:=1)])) then
      {
      winner := "me"
      insert(me,tmp)
      taken[tmp] := true
      display()
      done_yet()
      write(at(1,22),chop(&host)," Wins, You Loose!")
      every square := !row do writes(pointer[square],mark)
      again := map(input(at(1,23) || "Another game? Y/N :"))[1]
      if again=="y" then next
      stop(at(1,23),"Game Over.",chop())
      }

#
# user made a good move, continue (computer plays now)
#
    insert(me,(tmp := integer(path[(index+:=1)])))
    taken[tmp] := true
    display()
#
# player follows
#
    insert(you,(tmp := integer(get_your_move())))
    taken[integer(tmp)] := true
    your_last_move := tmp
    display()

#
# if didn't take position dictated, loss ensues
#
    if your_last_move ~= (tmp := integer(path[(index+:=1)])) then
      {
      winner := "me"
      insert(me,tmp)
      taken[tmp] := true
      display()
      done_yet()
      write(at(1,22),chop(&host)," Wins, You Loose!")
      every square := !row do writes(pointer[square],mark)
      again := map(input(at(1,23) || "Another game? Y/N :"))[1]
      if again=="y" then next
      stop(at(1,23),"Game Over.",chop())
      }
#
# if players first move wasn't 5, they lose now too
#
    if integer(path[2]) ~= 5 then
      {
      tmp := integer(path[(index+:=1)])
      winner := "me"
      insert(me,tmp)
      taken[tmp] := true
      display()
      done_yet()
      write(at(1,22),chop(&host)," Wins, You Loose!")
      every square := !row do writes(pointer[square],mark)
      again := map(input(at(1,23) || "Another game? Y/N :"))[1]
      if again=="y" then next
      stop(at(1,23),"Game Over.",chop())
      }

#
# user made a good move, continue (computer plays now)
#
    insert(me,(tmp := integer(path[(index+:=1)])))
    taken[tmp] := true
    display()
    write(at(1,22),chop(),"Game was a draw.")
    again := map(input(at(1,23) || "Another game? Y/N :"))[1]
    if again=="y" then next
    stop(at(1,23),"Game Over.",chop())
    }
  end
#
# procedure to display the current tictactoe grid and plays
#               
procedure display()
  local line, x, y

  if (pass +:= 1) = 1 then
    {
    write(cls(),uhalf(),"          T I C - T A C - T O E")
    write(lhalf(),"          T I C - T A C - T O E")
    write(trim(center("Computer is 'O' and you are 'X'",80)))
    line := repl("q",60) ; line[21] := "n" ; line[41] := "n"
    every y := 5 to 20 do writes(at(30,y),graf("x"))
    every y := 5 to 20 do writes(at(50,y),graf("x"))
    writes(at(10,10),graf(line))
    writes(at(10,15),graf(line))
    every x := 1 to 9  do writes(pointer[x],dim(x))
    }
  every writes(pointer[!me],high("O"))
  every writes(pointer[!you],under("X"))
  end
                
#
# procedure to obtain a move choice from the player
#
procedure get_your_move()
  local yours,all_moves
  repeat {
  writes(at(5,22))
  yours := input("Enter block # (1-9) :")
  writes(at(5,23),chop())
  if not(integer(yours)) then
    {
    writes(at(5,23),beep(),"Invalid Input! Choose 1-9.")
    next
    }
  if (1 > yours) | (yours > 9) then
    {
    writes(at(5,23),beep(),"Value out of range! Choose 1-9.")
    next
    }
  if taken[integer(yours)] == true then
    {
    writes(at(5,23),beep(),"That position is already taken! Try again.")
    next
    }
  break }
  return integer(yours)
  end

#
# procedure to test if computer has won, or the game is a draw
#
procedure done_yet()
  local outcome, test, part

  every outcome := !wins do
    {
    test := 0
    every part := !outcome do
      if member(you,part) then test +:= 1
    if test = 3 then
      {
      winner := "you"
      row    := outcome
      mark   := high(blink("X"))
      return true
      }
    }
  every outcome := !wins do
    {
    test := 0
    every part := !outcome do
      if member(me,part) then test +:= 1
    if test = 3 then
      {
      winner := "me"
      row    := outcome
      mark   := high(blink("O"))
      return true
      }             
    }
  if *me + *you > 8 then
    {
    winner := draw
    return draw
    }
  return "not done yet"
  end
#
# prompts for an input from the user
#
procedure input(prompt)
  writes(prompt)
  return read()
  end
#
# procedures to output ansi graphics and attributes
#
procedure at(x,y)
  return "\e[" || y || ";" || x || "f"
  end
  
procedure graf(str)
  return "\e(0" || str || "\e(B"
  end

procedure uhalf(str)
  /str := ""
  return "\e#3" || str
  end

procedure lhalf(str)
  /str := ""
  return "\e#4" || str
  end

procedure high(str)
  return "\e[1m" || str || "\e[0m"
  end

procedure normal(str)
  return "\e[0m" || str
  end

procedure dim(str)
  return "\e[2m" || str || "\e[0m"
  end
  
procedure under(str)
  return "\e[4m" || str || "\e[0m"
  end

procedure blink(str)
  return "\e[5m" || str || "\e[0m"
  end

procedure cls(str)
  /str := ""
  return "\e[2J\e[H" || str
  end

procedure chop(str)
  /str := ""
  return "\e[J" || str
  end

procedure beep()
  return "\7"
  end
#
# procedure to init useful global variables for later use
#
procedure init()
  true    := "y"
  false   := "n"
  draw    := "?"
  randomize()
  routes  := ["-","1274958","1374958","1432956","1547328",
                  "1632745","1732956","1874352","1974352"]
  wins    := [set([1,5,9]),set([3,5,7]),set([1,2,3]),set([4,5,6]),
              set([7,8,9]),set([1,4,7]),set([2,5,8]),set([3,6,9])]
  pointer := [at(17,7), at(37,7), at(57,7),
              at(17,12),at(37,12),at(57,12),
              at(17,17),at(37,17),at(57,17)]
  end
  

