Tictactoe

From Scriptwiki
Jump to: navigation, search
/*
tic-tac-toe by Vliedel #help.script @ QuakeNet

This script is a joke, yet fully functional
You can play it over IRC, or at your own client

Usage:

/tictac [nick] [N M]

where:
nick:  the nick on IRC you want to play against
N:     field size of N x N (default 3)
M:     number of crosses/circles you need to connect to win the game (default 3)

Notes:
-Yes it is slow, that was part of the joke: I draw each pixel seperately
-Yes you can cheat, I didn't include enough checks against cheating
-The script does not keep up nickchanges or anything like that
-If anyting gets stuck, or you want to stop, just: /hfree tictac
-You can change the number of the first 4 aliases to change the sizes of the drawings
*/

alias -l fontsize { return 15 }
alias -l pix { return 300 }
alias -l bordersize { return 10 }
alias -l innersize { return 7 }

alias tictac {
  ; if used as identifier, calculate and show the result
  if ($isid) {
    ; field size (square)
    var %field = $iif(($2 > 1) && (1 // $1),$2,3)
    ; you need to connect %win to win the game
    var %win = $iif(($3 > 1) && (1 // $1),$3,3)

    if ($len($1) !== $calc(%field ^2)) || ($calc($count($1,0) + $count($1,1) + $count($1,2)) < $v1) { return }

    ; check horizontal winnings
    var %h = $calc(%field - %win +1)
    while (%h) {
      var %i = $calc(%field * (%field - 1) + %h)
      while (%i > 0) {
        var %p = $mid($1,%i,1) , %w = %i
        if (%p) {
          var %j = $calc(%i + %win -1)
          while (%j > %i) {
            if ($mid($1,%j,1) !== %p) { var %p | break }
            var %w = %w %j
            dec %j
          }
          if (%p) { goto win }
        }
        dec %i %field
      }
      dec %h
    }

    ; check for vertical winnings
    var %h = $calc(%field - %win +1) , %i = $calc(%field * %h)
    while (%i) {
      var %p = $mid($1,%i,1) , %w = %i
      if (%p) {
        var %j = $calc(%i + %field * (%win -1))
        while (%j > %i) {
          if ($mid($1,%j,1) !== %p) { var %p | break }
          var %w = %w %j
          dec %j %field
        }
        if (%p) { goto win }
      }
      dec %i
    }

    ; check for diagonal winnings (up left to low right)
    var %h = $calc(%field - %win +1)
    while (%h) {
      var %i = $calc(%field * (%h -1) + %h)
      while (%i > 0) {
        var %p = $mid($1,%i,1) , %w = %i
        if (%p) {
          var %j = $calc(%i + (%win -1) * (%field +1))
          while (%j > %i) {
            if ($mid($1,%j,1) !== %p) { var %p | break }
            var %w = %w %j
            dec %j $calc(%field +1)
          }
          if (%p) { goto win }
        }
        dec %i %field
      }
      dec %h
    }

    ; check for diagonal winnings (up right to down left)
    var %h = $calc(%field - %win +1)
    while (%h) {
      var %i = $calc(%field * (%h -1) + %win + %h -1)
      while (%i > 0) {
        var %p = $mid($1,%i,1) , %w = %i
        if (%p) {
          var %j $calc(%i + (%win -1) * (%field -1))
          while (%j > %i) {
            if ($mid($1,%j,1) !== %p) { var %p | break }
            var %w = %w %j
            dec %j $calc(%field -1)
          }
          if (%p) { goto win }
        }
        dec %i %field
      }
      dec %h
    }

    ; no winner was found, return 0
    drawfield $1 %win
    return 0

    ; if a winning player has been found, we end up here with %p the winning player
    :win
    drawfield $1 %win %p %w
    return %p
  }
  else {
    ; start a tictactoe game
    ; [$1 = nick]  [$2 = field size   $3 = number of crosses/circles you need to connect to win the game]
    var %vs
    if (($1 != $null) && ($1 == $gettok(%tictactoe.req,1,32))) { tokenize 32 %tictactoe.req | var %vs = 1 | .notice $v2 -tictactoe accept }
    if ($comchan($1,1)) { var %nick = $iif($1 !== $me,$1) | tokenize 32 $2- }
    var %field = $iif(($1 > 1) && (1 // $1),$1,3)
    var %win = $iif(($2 > 1) && (1 // $2),$2,3)
    if (%win > %field) {
      echo -ag Usage: /tictac [nick] [N M]
      echo -ag nick: the nick on IRC you want to play against
      echo -ag N = field size of N x N (default 3)
      echo -ag M = number of crosses/circles you need to connect to win the game (default 3)
      return
    }
    if ($hget(tictac)) { hfree tictac }
    var %state = $str(0,$calc(%field ^2))
    hmake tictac
    hadd tictac turn 1
    if (%vs) { hadd tictac me 2 }
    elseif (%nick) { hadd tictac me 1 }
    hadd tictac field %field
    hadd tictac win %win
    hadd tictac state %state
    hadd tictac nick %nick
    if (!%vs) && (%nick) { .notice %nick -tictactoe %field %win }
    else { drawfield %state %win }
  }
}

on ^*:NOTICE:-tictactoe state &:?:{
  if ($hget(tictac,nick) == $nick) && ($sqrt($len($3)) == $hget(tictac,field))  {
    haltdef
    hadd tictac state $3
    noop $tictac($3,$hget(tictac,field),$hget(tictac,win))
  }
}

on ^*:NOTICE:-tictactoe & &:?:{
  haltdef
  var %field = $iif(($2 > 1) && (1 // $2),$2,3)
  var %win = $iif(($3 > 1) && (1 // $3),$3,3)
  set -u300 %tictactoe.req $nick %field %win
  echo -ag $+($chr(2),tictactoe,$chr(2)) $nick wants to play a game of tictactoe with you. The field is $+(%field,x,%field) and you need to connect %win to win.
  echo -ag $+($chr(2),tictactoe,$chr(2)) Use /tictac $nick to accept the game.
}

on ^*:NOTICE:-tictactoe accept:?:{
  if ($hget(tictac,nick) == $nick) {
    haltdef
    drawfield $hget(tictac,state) $hget(tictac,win)
  }
}

; $1 = positions   $2 = %win   [$3 = winning player   $4- = winning fields]
alias -l drawfield {
  var %field = $sqrt($len($1)) , %w = $+(@,tictactoe_,%field,_,$2)
  if ($window(%w)) { window -c %w }
  window -aBfp +d %w 50 50 $pix $calc($pix + $height(I,Lucida Console,$fontsize) + 10)

  ; draw border lines
  var %h = $bordersize - 1
  while (%h >= 0) {
    var %i = $calc($pix - %h -1) , %m = %i
    var %c = $rgb(255,$calc(255 - 128 / $int($bordersize /2) * %h),$calc(128 * ($bordersize - %h) / $bordersize))
    while (%i >= %h) {
      drawdot -r %w %c 1 %i %h %i %m %m %i %h %i
      dec %i
    }
    dec %h
  }

  ; fill field with the background color
  var %x = $bordersize - 1 , %xm = $calc($pix - $bordersize -1)
  var %ym = $calc($pix - $bordersize -1) , %c = $rgb(15,15,15)
  while (%x <= %xm) {
    var %y = %x
    while (%y <= %ym) {
      drawdot -r %w %c 1 %x %y %y %x
      inc %y
    }
    inc %x
  }

  ; draw inner lines (innersize must be odd?)
  var %h = %field - 1 , %innerpix = $calc($pix - 2 * $bordersize)
  var %tilepix = $calc((%innerpix - (%field -1) * $innersize) / %field)
  while (%h) {
    var %i = 0 - $int($calc($innersize / 2)) , %m = $int($calc($innersize / 2))
    while (%i <= %m) {
      var %y = $calc($pix - $bordersize -1) , %ym = $bordersize - 1
      var %x = $round($calc($bordersize -1 + %tilepix * %h + $innersize * (%h -1) + $ceil($calc($innersize /2)) + %i),0)
      var %c = $rgb($calc(128 - 128 / $int($innersize /2) * $abs(%i)),$calc(128 - 128 / $int($innersize /2) * $abs(%i)),255)
      while (%y >= %ym) {
        drawdot -r %w %c 1 %x %y %y %x
        dec %y
      }
      inc %i
    }
    dec %h
  }

  /*
  ; fill each tile with the background color
  var %h = %field , %c = $rgb(15,15,15)
  while (%h) {
    var %i = %field
    while (%i >= %h) {
      var %x = $round($calc($bordersize -1 + (%h -1) * (%tilepix + $innersize)),0) , %xm = $round($calc(%x + %tilepix),0)
      while (%x <= %xm) {
        var %y = $round($calc($bordersize -1 + (%i -1) * (%tilepix + $innersize)),0) , %ym = $round($calc(%y + %tilepix),0)
        while (%y <= %ym) {
          drawdot -r %w %c 1 %x %y %y %x
          inc %y
        }
        inc %x
      }
      dec %i
    }
    dec %h
  }
  */

  ; draw the crosses and circles
  var %h = %field
  while (%h) {
    var %i = %field
    while (%i) {
      var %pos = $calc((%h -1) * %field + %i) , %p = $mid($1,%pos,1)
      if (!%p) { dec %i | continue }
      var %xc = $round($calc($bordersize -1 + (%i -1) * (%tilepix + $innersize) + %tilepix / 2),0)
      var %yc = $round($calc($bordersize -1 + (%h -1) * (%tilepix + $innersize) + %tilepix / 2),0)
      if (%p == 1) {
        var %j = $round($calc(%tilepix * 0.4),0) , %c = $rgb(0,255,0)
        while (%j > -1) {
          drawdot -r %w %c 1 $calc(%xc + %j) $calc(%yc + %j) $calc(%xc - %j) $calc(%yc + %j) $calc(%xc + %j) $calc(%yc - %j) $calc(%xc - %j) $calc(%yc - %j)
          dec %j
        }
        if ($istok($4-,%pos,32)) {
          var %j = $round($calc(%tilepix * 0.3),0) , %c = $rgb(0,255,0)
          while (%j > -1) {
            drawdot -r %w %c 1 $calc(%xc + %j) %yc $calc(%xc - %j) %yc %xc $calc(%yc - %j) %xc $calc(%yc + %j)
            dec %j
          }
        }
      }
      elseif (%p == 2) {
        var %r = $round($calc(%tilepix * 0.4),0) , %c = $rgb(255,0,0) , %t = 360
        while (%t) {
          drawdot -r %w %c 1 $calc(%xc + %r * $cos(%t).deg) $calc(%yc + %r * $sin(%t).deg)
          dec %t
        }
        if ($istok($4-,%pos,32)) {
          var %r = $round($calc(%tilepix * 0.3),0) , %c = $rgb(255,0,0) , %t = 160
          while (%t >= 30) {
            drawdot -r %w %c 1 $calc(%xc + %r * $cos(%t).deg) $calc(%yc + %r * $sin(%t).deg)
            dec %t
          }
          var %r = $round($calc(%tilepix * 0.25),0)
          drawdot -r %w %c 3 $calc(%xc + %r * $cos(225).deg) $calc(%yc + %r * $sin(225).deg)
          drawdot -r %w %c 3 $calc(%xc + %r * $cos(315).deg) $calc(%yc + %r * $sin(315).deg)
        }
      }

      dec %i
    }
    dec %h
  }
  ;set turn to the correct value
  if ($hget(tictac)) { hadd tictac turn $iif($count($1,1) > $count($1,2),2,1) }

  ; display text
  if ($3) { var %t = Player $3 wins! | if ($hget(tictac)) { hfree tictac } }
  elseif (0 !isin $1) { var %t = Draw game! | if ($hget(tictac)) { hfree tictac } }
  elseif ($hget(tictac)) { var %t = Player $hget(tictac,turn) $+ 's turn }
  drawtext -r %w $rgb(255,128,0) "Lucida Console" $fontsize 5 $calc($pix +5) %t
}

menu @tictactoe_* {
  sclick:click $active $mouse.x $mouse.y
}

; $1 = window   $2 = x   $3 = y
alias -l click {
  if ($hget(tictac,me)) && ($v1 !== $hget(tictac,turn)) { return }
  var %field = $gettok($1,2,95) , %win = $gettok($1,3,95)
  if (%field !== $hget(tictac,field)) || (%win !== $hget(tictac,win)) { return }
  var %h = %field , %state = $hget(tictac,state)
  var %innerpix = $calc($pix - 2 * $bordersize)
  var %tilepix = $calc((%innerpix - (%field -1) * $innersize) / %field)
  while (%h) {
    var %i = %field
    while (%i) {
      var %pos = $calc((%h -1) * %field + %i) , %p = $mid(%state,%pos,1)
      if (!%p) {
        var %x = $round($calc($bordersize -1 + (%i -1) * (%tilepix + $innersize)),0)
        var %y = $round($calc($bordersize -1 + (%h -1) * (%tilepix + $innersize)),0)
        if ($inrect($2,$3,%x,%y,%tilepix,%tilepix)) {
          var %state = $+($left(%state,$calc(%pos -1)),$hget(tictac,turn),$mid(%state,$calc(%pos +1)))
          hadd tictac state %state
          if ($hget(tictac,nick)) { .notice $v1 -tictactoe state %state }
          noop $tictac(%state,%field,%win)
          return
        }
      }
      dec %i
    }
    dec %h
  }
}