#!/usr/bin/tclsh
# a cgi for playing against GnuGo
# Written in 1999 by Doug Ridgway
# This code is public domain: no copyright, no restrictions, no warranty.
# INSTALLATION
# o Install Tcl 8.0 or later (see http://www.scriptics.com/).
# o Install cgi.tcl 0.8.0 (from http://expect.nist.gov/)
# apply patch cgi.patch
# o Put script in a place accessible to your webserver, and set it up
# to be executed as a CGI.
# o Put patched cgi.tcl in same directory.
# o Set the GNUGO_PATH, DOMAIN, and PATH variables below.
# o Pick directory of desired images.
# The naming convention is consistent with Andrew Grant's GIFs,
# and his GIFs can be obtained at
# http://www.britgo.demon.co.uk/gopcres/gopcres1.html#ag-gifs
# Create a new directory, called "images", and unpack the gifs there.
# o Edit mumble file as desired.
# o Uncomment the cgi_debug -on command during debugging.
# CONFIGURATION
# location of GnuGo. Use full path.
set GNUGO_PATH /usr/local/bin/gnugo
#set GNUGO_PATH /usr/local/bin/pachi
# name of webserver (for cookie)
set DOMAIN ssl.ba.net
# location of script in web namespace (for cookie)
set PATH go/gg.cgi
# change these if you want different images
# what's needed: w,b,wt,bt,p,h,LS,RS,TS,BS,ULC,URL,LLC,LRC
# naming consistent with ag-gifs in sizes 55, 39, 27
# sizes 19 and 15 require slight futzing
set idir images
set iext .gif
source cgi.tcl
# don't change this unless you've got other mumble files available
set LANG en
# uncomment when debugging
# cgi_debug -on
# END OF CONFIGURATION -- code follows
source mumble.$LANG
set bug_list [list \
"Allows GnuGo to consume arbitrary amounts of CPU time"\
"Constant mumbling"\
]
# hoshi point offset / period
array set hoff {19 4 17 4 15 4 13 4 11 3 9 3 7 3 5 3}
array set hper {19 6 17 5 15 4 13 3 11 3 9 4 7 2 5 3}
proc im {img alt} {
global idir iext
put [img $idir/$img$iext "alt= $alt" width=19 height=19 border=0]
}
proc ib {img alt} {
global illegal
upvar i i j j
global idir iext
# shouldn't need extra quotes here: cgi.tcl bug?
if {[info exists illegal($i,$j)]} {
im $img $alt
} else {
image_button [ij2sgf $i $j]=$idir/$img$iext "alt=\" $alt\"" border=0
}
}
proc W {i j} {
global lm
if {"[ij2sgf $i $j]"=="$lm"} {
im wt O
} else {
im w O
}
}
proc B {i j} {
global lm
if {[ij2sgf $i $j]==$lm} {
im bt X
} else {
im b X
}
}
proc TW {i j} {
W $i $j
}
proc TB {i j} {
B $i $j
}
proc E {i j} {
global boardsize hoff hper
if {$i == 1 && $j == 1} {
ib ULC +
} elseif {$i == 1 && $j == $boardsize} {
ib URC +
} elseif {$i == $boardsize && $j == 1} {
ib LLC +
} elseif {$i == $boardsize && $j == $boardsize} {
ib LRC +
} elseif {$i == 1} {
ib TS -
} elseif {$i == $boardsize} {
ib BS -
} elseif {$j == 1} {
ib LS |
} elseif {$j == $boardsize} {
ib RS |
} elseif {($i-$hoff($boardsize))%$hper($boardsize) == 0 && ($j-$hoff($boardsize))%$hper($boardsize) == 0} {
ib h +
} else {
ib p .
}
}
proc print_board {} {
global boardsize
global board
center {
for {set i 1} {$i <= $boardsize} {incr i} {
for {set j 1} {$j <= $boardsize} {incr j} {
$board($i,$j) $i $j
}
br
}
}
}
proc sgf2ij {c} {
set c [string tolower $c]
binary scan $c cc j i
binary scan "a" c a
foreach v {i j} {
set $v [expr $$v+1-$a]
}
return $i,$j
}
proc ij2sgf {i j} {
binary scan "a" c a
foreach v {i j} {
set $v [expr $$v-1+$a]
}
binary format cc $j $i
}
proc new_board {{size 19}} {
global boardsize board illegal
set boardsize $size
for {set i 1} {$i <= $boardsize} {incr i} {
for {set j 1} {$j <= $boardsize} {incr j} {
set board($i,$j) E
}
}
catch {unset illegal}
}
proc scan_sgf {sgf} {
global board boardsize Size Color Handicap Komi illegal
cgi_debug {p "Scan SGF:
$sgf"}
set ttsgf $sgf
set sgf [gnugo simplify $sgf]
regexp -nocase {SZ\[([0-9]+)\]} $sgf {} boardsize
set Size $boardsize
new_board $boardsize
regexp -nocase {HA\[([0-9]+)\]} $sgf {} Handicap
regexp -nocase {KM\[(([0-9]|\.)+)\]} $sgf {} Komi
regexp -nocase {PL\[(W|B)\]} $sgf {} Color
if {"$Color" == "W"} {
set Color White
} {
set Color Black
}
while {[regexp {AW\[(..)\]} $sgf {} coords]} {
regsub {AW\[(..)\]} $sgf {AW} sgf
set board([sgf2ij $coords]) W
}
while {[regexp {AB\[(..)\]} $sgf {} coords]} {
regsub {AB\[(..)\]} $sgf {AB} sgf
set board([sgf2ij $coords]) B
}
while {[regexp {TW\[(..)\]} $ttsgf {} coords]} {
regsub {TW\[(..)\]} $ttsgf {TW} ttsgf
set board([sgf2ij $coords]) TW
}
while {[regexp {TB\[(..)\]} $ttsgf {} coords]} {
regsub {TB\[(..)\]} $ttsgf {TB} ttsgf
set board([sgf2ij $coords]) TB
}
while {[regexp {IL\[(..)\]} $sgf {} coords]} {
regsub {IL\[(..)\]} $sgf {IL} sgf
set illegal([sgf2ij $coords]) 1
}
cgi_debug {p "board is [array get board]"}
}
proc gnugo {cmd sgf} {
global GNUGO_PATH
cgi_debug {p "GG $cmd:
$sgf"}
switch $cmd {
play {
set f [open "| /usr/bin/nice -n 20 $GNUGO_PATH -l - --quiet --never-resign -o -" RDWR]
# set f [open "| /usr/bin/nice -n 20 $GNUGO_PATH --never-resign -l - --quiet -o -" RDWR]
}
score {
# set f [open "| $GNUGO_PATH -l - --quiet --score last" RDWR]
set f [open "| /usr/bin/nice -n 20 $GNUGO_PATH -l - --quiet --score end" RDWR]
# set f [open "| $GNUGO_PATH -l - --quiet --score end" RDWR]
}
simplify {
set f [open "| $GNUGO_PATH -l - --quiet --printsgf -" RDWR]
}
showterritory {
set f [open "| $GNUGO_PATH --quiet --score --analyzerfile -" RDWR]
# set f [open "| $GNUGO_PATH -l - --quiet --score --analyzerfile -" RDWR]
}
default {
cgi_debug {p "Unknown cmd $cmd"}
}
}
# dunno why I used to have to do this
if 0 {
fconfigure $f -blocking 0
puts $f $sgf
flush $f
set out ""
while {![eof $f]} {
append out [read $f]
}
cgi_debug {p "GG returns:
$out"}
return $out
}
puts $f $sgf
flush $f
set out [read $f]
cgi_debug {p "GG returns:
$out"}
return $out
}
proc mumble {event} {
global mumble
lindex $mumble($event) [expr int(rand()*[llength $mumble($event)])]
}
proc other {c} {
switch $c {
White {
return Black
}
Black {
return White
}
}
}
cgi_eval {
cgi_input
set msg ""
set lm ""
# first, deal with button presses
if {![catch {cgi_import zz}]} {
switch $zz {
Pass {
set mode pass
cgi_import sgf
regexp {PL\[(B|W)\]} [gnugo simplify $sgf] {} player
regsub {\)} $sgf ";$player\[\])" sgf
set sgf [gnugo play $sgf]
}
Score {
set mode score
cgi_import sgf
if {[catch {
cgi_import_cookie won
cgi_import_cookie lost}]} {
set won 0
set lost 0
}
set score [gnugo score $sgf]
set msg [format $message(score) $score]
cgi_import Color
if {[regexp -nocase "$Color (seems to )?win" $score]} {
append msg $message(youwin)
incr won
} elseif {[regexp -nocase "[other $Color] (seems to )?win" $score]} {
append msg [format $message(iwin) [mumble advice]]
incr lost
if {$lost > $won + 3 && $won/($lost+$won) < 0.4} {
switch $Color {
Black {
append msg $message(upyours)
}
White {
append msg $message(downmine)
}
}
}
if {$won > $lost + 3 && $won/($lost+$won) > 0.75} {
switch $Color {
Black {
append msg $message(downyours)
}
White {
append msg $message(upmine)
}
}
}
} else {
append msg $message(jigo)
}
append msg [format $message(stats) $won $lost]
append msg $message(another)
#jaime
# set sgf [gnugo showterritory $sgf]
}
Save {
cgi_import sgf
http_head {
content_type application/x-go-sgf
}
puts $sgf
exit
}
Load {
set mode load
set local [cgi_import_filename -local sgf_file]
set f [open $local]
set sgf [read $f]
close $f
file delete $local
set msg $message(restart)
}
New {
# get old values
cgi_import sgf
scan_sgf $sgf
set changed 0
foreach v {Size Handicap Color Komi} {
if {[set $v] != [cgi_import $v]} {
set changed 1
}
}
if {"$Komi"==""} {set Komi 5.5}
set sgf [format \
{(;GM[1]FF[3]RU[Japanese]SZ[%d]HA[%d]KM[%.1f]} \
$Size $Handicap $Komi]
if {$Handicap} {
append sgf {PL[W])}
} else {
append sgf {PL[B])}
}
if {"$Color" == "White" && $Handicap == 0
|| "$Color" == "Black" && $Handicap > 0} {
set sgf [gnugo play $sgf]
}
set mode new
if $changed {
append msg $message(handichange)
}
append msg [format $message(new) [mumble new]]
}
}
} elseif {![catch {cgi_import sgf}]} {
# Next, clicking on an empty vertex means a move
set mode play
set l [cgi_import_list]
set mn [lsearch $l {[a-z][a-z].x}]
set move [string range [lindex $l $mn] 0 1]
regexp {PL\[(B|W)\]} [gnugo simplify $sgf] {} player
regsub {\)} $sgf ";$player\[$move\])" sgf
set sgf [gnugo play $sgf]
} elseif {![catch {cgi_import_cookie sgf} err]} {
# old game stored in a cookie?
set mode load
set msg [format $message(resume) [exec $GNUGO_PATH --version |& cat]]
} else {
# A newbie! Give them an game to play...
set mode play
set msg [format $message(newbie) [exec $GNUGO_PATH --version |& cat]]
set sgf {(;GM[1]FF[3]RU[Japanese]SZ[9]HA[4]KM[0.5])}
set sgf [gnugo play $sgf]
}
# only for old buggy cgi.tcl
#jaime
regsub -all "\[ \r\n\]+" $sgf {} sgf
#jaime
cgi_http_head {
cgi_content_type text/html
cgi_export_cookie sgf domain=$DOMAIN path=$PATH expires=never
if {"$mode" == "new" && $changed} {
cgi_cookie_set won=0 domain=$DOMAIN path=$PATH expires=never
cgi_cookie_set lost=0 domain=$DOMAIN path=$PATH expires=never
} elseif {"$mode" == "score"} {
cgi_export_cookie won domain=$DOMAIN path=$PATH expires=never
cgi_export_cookie lost domain=$DOMAIN path=$PATH expires=never
}
}
scan_sgf $sgf
cgi_title {BA.net Go Baduk Board Game - Quick Go Play Online No Registration No Login - GnuGo 3.8 }
#jaime header
puts "
Computer Go GnuGo 3.8 "
cgi_body bgcolor=#F5F0F2 {
if {$mode=="play" || $mode=="pass"} {
unset lm
regexp {\[([^]]*)\][^]]*$} $sgf {} lm
if {![info exists lm]} {
cgi_debug {p "No Move found in $sgf! WTF???"}
} elseif {"$lm"=="" || ("$lm"=="tt" && $boardsize <=19)} {
if {$mode == "pass"} {
append msg $message(pass2)
} else {
append msg $message(pass1)
}
} else {
cgi_debug {p "Last move is $lm."}
append msg [format $message(move) [mumble move]]
}
}
switch $mode {
default {
p $msg
}
}
cgi_form gg {
print_board
center {
#jaime
p
br
cgi_submit_button zz=Pass
#puts " . "
cgi_submit_button zz=Score
#puts " . "
cgi_submit_button zz=Save
#puts " . "
#puts ""
#window.history.back();
#parent.window.history.back();
}
export sgf=$sgf
# hr
br
br
br
center {
put [bold "New game: "]
put Size
cgi_select Size {
for {set i 5} {$i <= 19} {incr i 2} {
option $i selected_if_equal=$Size
}
}
put Handicap
cgi_select Handicap {
foreach i {0 2 3 4 5 6 7 8 9} {
option $i selected_if_equal=$Handicap
}
}
put "Your Color"
cgi_select Color {
foreach c {White Black} {
option $c selected_if_equal=$Color
}
}
put "Komi"
cgi_text Komi=$Komi size=3
submit_button zz=New
br
}
}
cgi_form gg enctype=multipart/form-data {
center {
br
br
put [bold "Restart saved game:"]
cgi_file_button sgf_file
submit_button zz=Load
puts " |