[Tkabber-dev] r947 - in trunk/tkabber-plugins: . renju renju/msgs renju/pixmaps renju/pixmaps/stones

tkabber-svn at jabber.ru tkabber-svn at jabber.ru
Thu Feb 15 23:35:11 MSK 2007


Author: sergei
Date: 2007-02-15 23:35:04 +0300 (Thu, 15 Feb 2007)
New Revision: 947

Added:
   trunk/tkabber-plugins/renju/
   trunk/tkabber-plugins/renju/msgs/
   trunk/tkabber-plugins/renju/msgs/ru.msg
   trunk/tkabber-plugins/renju/pixmaps/
   trunk/tkabber-plugins/renju/pixmaps/stones/
   trunk/tkabber-plugins/renju/pixmaps/stones/b.gif
   trunk/tkabber-plugins/renju/pixmaps/stones/bot.gif
   trunk/tkabber-plugins/renju/pixmaps/stones/center.gif
   trunk/tkabber-plugins/renju/pixmaps/stones/icondef.xml
   trunk/tkabber-plugins/renju/pixmaps/stones/left.gif
   trunk/tkabber-plugins/renju/pixmaps/stones/left_bot.gif
   trunk/tkabber-plugins/renju/pixmaps/stones/left_top.gif
   trunk/tkabber-plugins/renju/pixmaps/stones/middle.gif
   trunk/tkabber-plugins/renju/pixmaps/stones/right.gif
   trunk/tkabber-plugins/renju/pixmaps/stones/right_bot.gif
   trunk/tkabber-plugins/renju/pixmaps/stones/right_top.gif
   trunk/tkabber-plugins/renju/pixmaps/stones/top.gif
   trunk/tkabber-plugins/renju/pixmaps/stones/w.gif
   trunk/tkabber-plugins/renju/proto
   trunk/tkabber-plugins/renju/renju.tcl
Modified:
   trunk/tkabber-plugins/ChangeLog
Log:
	* renju/*: New game plugin Gomoku/Renju (unfinished yet, only
	  free-style Gomoku is implemented).


Modified: trunk/tkabber-plugins/ChangeLog
===================================================================
--- trunk/tkabber-plugins/ChangeLog	2007-02-14 21:18:51 UTC (rev 946)
+++ trunk/tkabber-plugins/ChangeLog	2007-02-15 20:35:04 UTC (rev 947)
@@ -1,3 +1,8 @@
+2006-02-15  Sergei Golovan <sgolovan at nes.ru>
+
+	* renju/*: New game plugin Gomoku/Renju (unfinished yet, only
+	  free-style Gomoku is implemented).
+
 2006-02-14  Sergei Golovan <sgolovan at nes.ru>
 
 	* tclchat/tclchat.tcl: Added customize group Tclchat.

Added: trunk/tkabber-plugins/renju/msgs/ru.msg
===================================================================
--- trunk/tkabber-plugins/renju/msgs/ru.msg	                        (rev 0)
+++ trunk/tkabber-plugins/renju/msgs/ru.msg	2007-02-15 20:35:04 UTC (rev 947)
@@ -0,0 +1,54 @@
+
+::msgcat::mcset ru "Plugins options." "Параметры расширений."
+::msgcat::mcset ru "Show last move by default." "По умолчанию показывать последний ход."
+::msgcat::mcset ru " (You)" " (Вы)"
+::msgcat::mcset ru " (Opponent)" " (Оппонент)"
+::msgcat::mcset ru "Gomoku/Renju plugin options." "Параметры расширения Гомоку/Рэндзю."
+::msgcat::mcset ru "Gomoku/Renju figures theme." "Тема фишек Гомоку/Рэндзю."
+::msgcat::mcset ru "Free-style Gomoku" "Свободное Гомоку"
+::msgcat::mcset ru "Standard Gomoku" "Стандартное Гомоку"
+::msgcat::mcset ru "Renju" "Рэндзю"
+::msgcat::mcset ru "Show tooltips with short instructions." "Показывать всплывающие подсказки к элементам интерфейса."
+::msgcat::mcset ru "Allow illegal moves (useful for debugging)." "Разрешать неправильные ходы (используется при отладке)."
+::msgcat::mcset ru "Accept opponent illegal moves (useful for debugging)." "Принимать неправильные ходы от оппонента (используется при отладке)."
+::msgcat::mcset ru "Default game variant." "Вариант игры по умолчанию."
+::msgcat::mcset ru "Gomoku/Renju Invitation" "Приглашение сыграть в Гомоку/Рэндзю"
+::msgcat::mcset ru "Sending Gomoku/Renju game invitation to %s (%s)" "Посылаем приглашение сыграть в Гомоку/Рэндзю с %s (%s)"
+::msgcat::mcset ru "I want to move first" "Хочу ходить первым"
+::msgcat::mcset ru "I want to move second" "Хочу ходить вторым"
+::msgcat::mcset ru "Cancel invitation" "Отменить приглашение"
+::msgcat::mcset ru "%s (%s) has refused Gomoku/Renju invitation: %s" "%s (%s) отказался играть в Гомоку/Рэндзю: %s"
+::msgcat::mcset ru "Gomoku/Renju Invitation from %s" "Приглашение сыграть в Гомоку/Рэндзю с %s"
+::msgcat::mcset ru "Gomoku/Renju game invitation from %s (%s) is received." "Получено приглашение сыграть в Гомоку/Рэндзю с %s (%s)"
+::msgcat::mcset ru "%s wants play %s." "%s хочет играть в %s"
+::msgcat::mcset ru "%s wants to move first." "%s хочет ходить первым."
+::msgcat::mcset ru "%s wants to move second." "%s хочет ходить вторым."
+::msgcat::mcset ru "Agree to play" "Согласиться играть"
+::msgcat::mcset ru "Refuse to play" "Отказаться играть"
+::msgcat::mcset ru "%s with %s" "%s с %s"
+::msgcat::mcset ru "Show last move" "Показывать последний ход"
+::msgcat::mcset ru "Move: " "Ход: "
+::msgcat::mcset ru "Propose a draw" "Предложить ничью"
+::msgcat::mcset ru "Accept the draw proposal" "Принять предложенную ничью"
+::msgcat::mcset ru "Resign the game" "Сдать партию"
+::msgcat::mcset ru "History" "Запись партии"
+::msgcat::mcset ru "Gomoku/Renju..." "Гомоку/Рэндзю..."
+::msgcat::mcset ru "White" "Белые"
+::msgcat::mcset ru "Black" "Чёрные"
+::msgcat::mcset ru "Opponent wins" "Оппонент выиграл"
+::msgcat::mcset ru "You win" "Вы выиграли"
+::msgcat::mcset ru "Opponent wins (You resigned)" "Оппонент выиграл (Вы сдались)"
+::msgcat::mcset ru "You win (Opponent resigned)" "Вы выиграли (Оппонент сдался)"
+::msgcat::mcset ru "Draw" "Ничья"
+::msgcat::mcset ru "Draw (Both players skipped move)" "Ничья (Оба игрока пропустили ход)"
+::msgcat::mcset ru "Draw (You accepted)" "Ничья (Вы приняли)"
+::msgcat::mcset ru "Draw (Opponent accepted)" "Ничья (Оппонент принял)"
+::msgcat::mcset ru "Press button and make move if you want propose draw" "Нажмите кнопку и сделайте ход, если хотите предложить ничью"
+::msgcat::mcset ru "Press button if you want accept the draw proposal" "Нажмите кнопку, если хотите принять предложенную ничью"
+::msgcat::mcset ru "Press button if you want resign" "Нажмите кнопку, если хотите сдаться"
+::msgcat::mcset ru "Press button if you want skip current move" "Нажмите кнопку, если хотите пропустить ход"
+::msgcat::mcset ru "Skip the move" "Пропустить ход"
+::msgcat::mcset ru "\n\n Opponent proposes a draw\n\n" "\n\n Оппонент предлагает ничью\n\n"
+::msgcat::mcset ru "\n\n Opponent rejected move:\n %s\n\n" "\n\n Оппонент отверг ход:\n %s\n\n"
+::msgcat::mcset ru "Games" "Игры"
+::msgcat::mcset ru "Sound to play after opponent's turn" "Звук, который проигрывается после хода оппонента"


Property changes on: trunk/tkabber-plugins/renju/msgs/ru.msg
___________________________________________________________________
Name: svn:keywords
   + Author Date Id Revision
Name: svn:eol-style
   + native

Added: trunk/tkabber-plugins/renju/pixmaps/stones/b.gif
===================================================================
(Binary files differ)


Property changes on: trunk/tkabber-plugins/renju/pixmaps/stones/b.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/tkabber-plugins/renju/pixmaps/stones/bot.gif
===================================================================
(Binary files differ)


Property changes on: trunk/tkabber-plugins/renju/pixmaps/stones/bot.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/tkabber-plugins/renju/pixmaps/stones/center.gif
===================================================================
(Binary files differ)


Property changes on: trunk/tkabber-plugins/renju/pixmaps/stones/center.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/tkabber-plugins/renju/pixmaps/stones/icondef.xml
===================================================================
--- trunk/tkabber-plugins/renju/pixmaps/stones/icondef.xml	                        (rev 0)
+++ trunk/tkabber-plugins/renju/pixmaps/stones/icondef.xml	2007-02-15 20:35:04 UTC (rev 947)
@@ -0,0 +1,58 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<icondef>
+  <meta>
+    <name>Stones</name>
+    <version>1.0</version>
+    <description>Renju Stones Theme.</description>
+    <creation>2007-02-15</creation>
+  </meta>
+  <icon>
+    <image xmlns='tkimage'>renju/b</image>
+    <object mime="image/gif">b.gif</object>
+  </icon>
+  <icon>
+    <image xmlns='tkimage'>renju/w</image>
+    <object mime="image/gif">w.gif</object>
+  </icon>
+  <icon>
+    <image xmlns='tkimage'>renju/bf</image>
+    <object mime="image/gif">bot.gif</object>
+  </icon>
+  <icon>
+    <image xmlns='tkimage'>renju/tf</image>
+    <object mime="image/gif">top.gif</object>
+  </icon>
+  <icon>
+    <image xmlns='tkimage'>renju/rf</image>
+    <object mime="image/gif">right.gif</object>
+  </icon>
+  <icon>
+    <image xmlns='tkimage'>renju/lf</image>
+    <object mime="image/gif">left.gif</object>
+  </icon>
+  <icon>
+    <image xmlns='tkimage'>renju/rbf</image>
+    <object mime="image/gif">right_bot.gif</object>
+  </icon>
+  <icon>
+    <image xmlns='tkimage'>renju/rtf</image>
+    <object mime="image/gif">right_top.gif</object>
+  </icon>
+  <icon>
+    <image xmlns='tkimage'>renju/lbf</image>
+    <object mime="image/gif">left_bot.gif</object>
+  </icon>
+  <icon>
+    <image xmlns='tkimage'>renju/ltf</image>
+    <object mime="image/gif">left_top.gif</object>
+  </icon>
+  <icon>
+    <image xmlns='tkimage'>renju/mf</image>
+    <object mime="image/gif">middle.gif</object>
+  </icon>
+  <icon>
+    <image xmlns='tkimage'>renju/cf</image>
+    <object mime="image/gif">center.gif</object>
+  </icon>
+</icondef>
+


Property changes on: trunk/tkabber-plugins/renju/pixmaps/stones/icondef.xml
___________________________________________________________________
Name: svn:keywords
   + Author Date Id Revision
Name: svn:eol-style
   + native

Added: trunk/tkabber-plugins/renju/pixmaps/stones/left.gif
===================================================================
(Binary files differ)


Property changes on: trunk/tkabber-plugins/renju/pixmaps/stones/left.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/tkabber-plugins/renju/pixmaps/stones/left_bot.gif
===================================================================
(Binary files differ)


Property changes on: trunk/tkabber-plugins/renju/pixmaps/stones/left_bot.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/tkabber-plugins/renju/pixmaps/stones/left_top.gif
===================================================================
(Binary files differ)


Property changes on: trunk/tkabber-plugins/renju/pixmaps/stones/left_top.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/tkabber-plugins/renju/pixmaps/stones/middle.gif
===================================================================
(Binary files differ)


Property changes on: trunk/tkabber-plugins/renju/pixmaps/stones/middle.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/tkabber-plugins/renju/pixmaps/stones/right.gif
===================================================================
(Binary files differ)


Property changes on: trunk/tkabber-plugins/renju/pixmaps/stones/right.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/tkabber-plugins/renju/pixmaps/stones/right_bot.gif
===================================================================
(Binary files differ)


Property changes on: trunk/tkabber-plugins/renju/pixmaps/stones/right_bot.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/tkabber-plugins/renju/pixmaps/stones/right_top.gif
===================================================================
(Binary files differ)


Property changes on: trunk/tkabber-plugins/renju/pixmaps/stones/right_top.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/tkabber-plugins/renju/pixmaps/stones/top.gif
===================================================================
(Binary files differ)


Property changes on: trunk/tkabber-plugins/renju/pixmaps/stones/top.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/tkabber-plugins/renju/pixmaps/stones/w.gif
===================================================================
(Binary files differ)


Property changes on: trunk/tkabber-plugins/renju/pixmaps/stones/w.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/tkabber-plugins/renju/proto
===================================================================
--- trunk/tkabber-plugins/renju/proto	                        (rev 0)
+++ trunk/tkabber-plugins/renju/proto	2007-02-15 20:35:04 UTC (rev 947)
@@ -0,0 +1,84 @@
+Request:
+
+<iq type='set' to='a at b.c' id='id1'>
+    <create xmlns='games:board' id='123' type='renju' board='8x8' color='white'>
+	<timelimit>600</timelimit>   -- for all players
+	<timelimit color='black'>10</timelimit>  
+                                     -- if on black side played Kasparov
+	<initboard>
+	    <set pos='0,0' color='white'/>
+	    <set pos='1,0' color='white'/>
+	</initboard>
+    </create>
+</iq>
+
+Positive response:
+
+<iq type='result' to='x at y.z' id='id1'>
+    <create xmlns='games:board' type='renju' id='123'/>
+</iq>
+
+or simply <iq type='result' id='id1'/>?
+
+
+
+Turn:
+
+<iq type='set' to='a at b.c' id='id2'>
+    <turn xmlns='games:board' type='renju' id='123'>
+	<put pos='1,6'/>
+    </turn>
+</iq>
+
+'pos' attribute contains coordinates of piece.
+Coordinate is a comma-separated list of integers >= 0.
+
+Skipping the move:
+
+<iq type='set' to='a at b.c' id='id3'>
+    <turn xmlns='games:board' type='renju' id='123'>
+	<skip/>
+    </turn>
+</iq>
+
+Proposing draw:
+
+<iq type='set' to='a at b.c' id='id4'>
+    <turn xmlns='games:board' type='renju' id='123'>
+	<put pos='1,7'/>
+	<draw/>
+    </turn>
+</iq>
+
+Accepting draw proposal:
+
+<iq type='set' to='a at b.c' id='id5'>
+    <turn xmlns='games:board' type='renju' id='123'>
+	<accept/>
+    </turn>
+</iq>
+
+Resigning:
+
+<iq type='set' to='a at b.c' id='id6'>
+    <turn xmlns='games:board' type='renju' id='123'>
+	<resign/>
+    </turn>
+</iq>
+
+Also we need <put pos='2,10'/> and <destroy pos='2,10'/> tags for games where
+we must put or remove pieces.  <put/> can have 'type' and 'color'
+attributes.
+
+move, put & destroy tags may have some game-specific subtags.
+
+
+
+Response:
+
+<iq type='result' to='x at y.z' id='id2'/>
+
+if move accepted, or error 'Not Acceptable' if other side thinks that this move
+illegal.
+
+


Property changes on: trunk/tkabber-plugins/renju/proto
___________________________________________________________________
Name: svn:keywords
   + Author Date Id Revision
Name: svn:eol-style
   + native

Added: trunk/tkabber-plugins/renju/renju.tcl
===================================================================
--- trunk/tkabber-plugins/renju/renju.tcl	                        (rev 0)
+++ trunk/tkabber-plugins/renju/renju.tcl	2007-02-15 20:35:04 UTC (rev 947)
@@ -0,0 +1,1287 @@
+# $Id: renju.tcl$
+
+package require msgcat
+
+namespace eval renju {
+    variable scriptdir [file dirname [info script]]
+
+    ::msgcat::mcload [file join $scriptdir msgs]
+
+    variable square_size 31
+    variable line_width 1
+
+    variable themes
+    set dirs [glob -nocomplain -directory [file join $scriptdir pixmaps] *]
+    foreach dir $dirs {
+	pixmaps::load_theme_name [namespace current]::themes $dir
+    }
+    set values {}
+    foreach theme [lsort [array names themes]] {
+	lappend values $theme $theme
+    }
+
+#    set game_names_list \
+#	[list \
+#	    gomoku:freestyle  [::msgcat::mc "Free-style Gomoku"] \
+#	    gomoku:standard   [::msgcat::mc "Standard Gomoku"] \
+#	    renju             [::msgcat::mc "Renju"] \
+#	]
+    set game_names_list \
+	[list \
+	    gomoku:freestyle  [::msgcat::mc "Free-style Gomoku"] \
+	]
+    array set game_names $game_names_list
+
+    custom::defgroup Plugins [::msgcat::mc "Plugins options."] -group Tkabber
+
+    custom::defgroup Gomoku/Renju [::msgcat::mc "Gomoku/Renju plugin options."] -group Plugins
+
+    custom::defvar options(theme) Stones \
+	[::msgcat::mc "Gomoku/Renju figures theme."] -group Gomoku/Renju \
+	-type options -values $values \
+	-command [namespace current]::load_stored_theme
+    custom::defvar options(game) gomoku:freestyle \
+	[::msgcat::mc "Default game variant."] -group Gomoku/Renju \
+	-type options \
+	-values $game_names_list
+    custom::defvar options(show_last_move) 0 \
+	[::msgcat::mc "Show last move by default."] \
+	-type boolean -group Gomoku/Renju
+    custom::defvar options(show_tooltips) 1 \
+	[::msgcat::mc "Show tooltips with short instructions."] \
+	-type boolean -group Gomoku/Renju \
+	-command [list [namespace current]::set_tooltips]
+    custom::defvar options(sound) "" \
+	[::msgcat::mc "Sound to play after opponent's turn"] \
+	-type file -group Gomoku/Renju
+    custom::defvar options(allow_illegal) 0 \
+	[::msgcat::mc "Allow illegal moves (useful for debugging)."] \
+	-type boolean -group Gomoku/Renju
+    custom::defvar options(accept_illegal) 0 \
+	[::msgcat::mc "Accept opponent illegal moves (useful for debugging)."] \
+	-type boolean -group Gomoku/Renju
+}
+
+proc renju::load_stored_theme {args} {
+    variable options
+    variable themes
+
+    pixmaps::load_dir $themes($options(theme))
+}
+
+hook::add postload_hook [namespace current]::renju::load_stored_theme 70
+
+proc renju::get_nick {connid jid type} {
+    if {[catch {chat::get_nick $connid $jid $type} nick]} {
+	return [chat::get_nick $jid $type]
+    } else {
+	return $nick
+    }
+}
+
+proc renju::invite_dialog {connid jid} {
+    variable options
+
+    set w .renju_invite
+
+    if {[winfo exists $w]} {
+	destroy $w
+    }
+
+    Dialog $w -title [::msgcat::mc "Gomoku/Renju Invitation"] \
+	-separator 1 -anchor e -default 0
+
+    set wf [$w getframe]
+    message $wf.message -aspect 50000 \
+	-text [format [::msgcat::mc "Sending Gomoku/Renju game invitation to %s (%s)"] \
+		      [get_nick $connid $jid chat] \
+		      $jid]
+
+    pack $wf.message -pady 2m
+
+    variable game $options(game)
+    radiobutton $wf.freestyle -text [::msgcat::mc "Free-style Gomoku"] \
+	-value gomoku:freestyle -variable [namespace current]::game
+    pack $wf.freestyle -padx 15m -anchor w
+    radiobutton $wf.standard -text [::msgcat::mc "Standard Gomoku"] \
+	-state disabled -value gomoku:standard -variable [namespace current]::game
+    pack $wf.standard -padx 15m -anchor w
+    radiobutton $wf.renju -text [::msgcat::mc "Renju"] \
+	-state disabled -value renju -variable [namespace current]::game
+    pack $wf.renju -padx 15m -anchor w
+
+    $w add -text [::msgcat::mc "I want to move first"] \
+	-command [list [namespace current]::invite $connid $jid black]
+    $w add -text [::msgcat::mc "I want to move second"] \
+	-command [list [namespace current]::invite $connid $jid white]
+    $w add -text [::msgcat::mc "Cancel invitation"] \
+	-command [list destroy $w]
+
+    $w draw
+}
+
+proc renju::invite {connid jid color} {
+    variable game
+
+    destroy .renju_invite
+
+    set id renju[random 1000000000]
+
+    # FIX
+    #set rjid [get_jid_of_user $jid]
+
+    jlib::send_iq set \
+	[jlib::wrapper:createtag create \
+	     -vars [list xmlns games:board type $game id $id color $color]] \
+	-to $jid \
+	-command [list [namespace current]::invite_res $game $connid $jid $id $color] \
+	-connection $connid
+}
+
+proc renju::invite_res {game connid jid id color res child} {
+    if {![cequal $res OK]} {
+	after idle [list NonmodalMessageDlg .renju_invite_error -aspect 50000 -icon error \
+	    -message [format [::msgcat::mc "%s (%s) has refused Gomoku/Renju invitation: %s"] \
+			     [get_nick $connid $jid chat] \
+			     $jid [error_to_string $child]]]
+	return ""
+    }
+
+    start_play $game $connid $jid $id $color
+}
+
+
+proc renju::invited_dialog {game connid jid id color} {
+    variable game_names
+
+    set w .renju_invited
+
+    if {[winfo exists $w]} {
+	destroy $w
+    }
+
+    Dialog $w -title [format [::msgcat::mc "Gomoku/Renju Invitation from %s"] $jid] \
+	-separator 1 -anchor e -default 0
+
+    set wf [$w getframe]
+    set nick [get_nick $connid $jid chat]
+    set message1 [format [::msgcat::mc "Gomoku/Renju game invitation from %s (%s) is received."] \
+			 $nick $jid]
+    set message2 [format [::msgcat::mc "%s wants play %s."] $nick $game_names($game)]
+    switch -- $color {
+	white {
+	    set message3 [format [::msgcat::mc "%s wants to move second."] $nick]
+	}
+	black {
+	    set message3 [format [::msgcat::mc "%s wants to move first."] $nick]
+	}
+	default {
+	    return [list error modify bad-request]
+	}
+    }
+    message $wf.message1 -aspect 50000 -text $message1
+    message $wf.message2 -aspect 50000 -text $message2
+    message $wf.message3 -aspect 50000 -text $message3
+    pack $wf.message1 -pady 1m
+    pack $wf.message2 -pady 1m
+    pack $wf.message3 -pady 1m
+    
+    $w add -text [::msgcat::mc "Agree to play"]
+    $w add -text [::msgcat::mc "Refuse to play"]
+
+    if {[$w draw] == 0} {
+	destroy .renju_invited
+	switch -- $color {
+	    white {
+		start_play $game $connid $jid $id black
+	    }
+	    black {
+		start_play $game $connid $jid $id white
+	    }
+	    default {
+		return [list error modify bad-request]
+	    }
+	}
+
+	return [list result\
+		     [jlib::wrapper:createtag create \
+			  -vars [list xmlns games:board type $game id $id]]]
+    } else {
+	return [list error modify not-acceptable]
+    }
+}
+
+proc renju::start_play {game connid jid id color} {
+
+    set gid [make_gid $jid $id]
+    variable $gid
+    upvar 0 $gid flags
+
+    set flags(window) [win_id renju $gid]
+    set flags(connid) $connid
+    set flags(opponent) $jid
+    set flags(id) $id
+    set flags(game) $game
+    set flags(our_color) $color
+
+    trace variable [namespace current]::${gid}(position,turn) w \
+	[list [namespace current]::set_label_move $gid]
+
+    make_default_position $gid
+
+    open $gid
+}
+
+proc renju::set_label_move {gid args} {
+    variable $gid
+    upvar 0 $gid flags
+
+    switch -- $flags(position,turn) {
+	white {
+	    set flags(move_label) [::msgcat::mc "White"]
+	    set move 1
+	}
+	black {
+	    set flags(move_label) [::msgcat::mc "Black"]
+	    set move 1
+	}
+	default {
+	    set move 0
+	}
+    }
+    if {$move && [is_my_move $gid]} {
+	append flags(move_label) [::msgcat::mc " (You)"]
+    } else {
+	append flags(move_label) [::msgcat::mc " (Opponent)"]
+    }
+}
+
+proc renju::make_default_position {gid} {
+    variable $gid
+    upvar 0 $gid flags
+
+    for {set c 0} {$c < 15} {incr c} {
+	for {set r 0} {$r < 15} {incr r} {
+	    set flags(position,$c,$r) ""
+	}
+    }
+
+    set flags(position,turn) black
+
+    catch {unset flags(position,last_move)}
+    set flags(position,draw) 0
+    set flags(position,halfmove) 0
+    set flags(position,history) {}
+}
+
+proc renju::save_position {gid} {
+    variable $gid
+    upvar 0 $gid flags
+
+    set flags(saved_position) [array get flags position,*]
+}
+
+proc renju::restore_position {gid} {
+    variable $gid
+    upvar 0 $gid flags
+
+    array set flags $flags(saved_position)
+    draw_position $gid
+    update_controls $gid
+    find_legal_moves $gid $flags(position,turn)
+}
+
+proc renju::make_gid {jid id} {
+    jid_to_tag [concat $jid $id]
+}
+
+proc renju::turn_recv {gid children} {
+    variable options
+    variable $gid
+    upvar 0 $gid flags
+
+    set move 0
+    set skip 0
+    set draw 0
+
+    foreach child $children {
+	jlib::wrapper:splitxml $child tag vars isempty chdata children1
+	switch -- $tag {
+	    put {
+		set pos [jlib::wrapper:getattr $vars pos]
+		set poss [split $pos ","]
+		if {[llength $poss] == 2} {
+		    set ct [lindex $poss 0]
+		    set rt [lindex $poss 1]
+		    set move 1
+		    if {$options(sound) != "" && ![::sound::is_mute]} {
+			::sound::play $options(sound)
+		    }
+		}
+	    }
+	    skip {
+		set skip 1
+		add_move_to_history $gid
+		if {[is_white $flags(position,turn)]} {
+		    set flags(position,turn) black
+		} else {
+		    set flags(position,turn) white
+		}
+	    }
+	    resign {
+		end_game $gid 1 [::msgcat::mc "You win (Opponent resigned)"]
+		update_controls $gid
+		draw_position $gid
+		highlight_last_move $gid
+		return [list result [jlib::wrapper:createtag turn \
+					 -vars [list xmlns games:board \
+						     type $flags(game) \
+						     id $flags(id)]]]
+	    }
+	    accept {
+		if {$flags(position,draw)} {
+		    end_game $gid 0.5 [::msgcat::mc "Draw (Opponent accepted)"]
+		    update_controls $gid
+		    draw_position $gid
+		    highlight_last_move $gid
+		    return [list result [jlib::wrapper:createtag turn \
+					     -vars [list xmlns games:board \
+							 type $flags(game) \
+							 id $flags(id)]]]
+		} else {
+		    return [list error modify not-acceptable]
+		}
+	    }
+	    draw {
+		set draw 1
+	    }
+	}
+    }
+
+    if {$skip || ($move && [do_move $gid $ct $rt $draw])} {
+	if {[lindex $flags(position,history) end] == "skip" && \
+		[lindex $flags(position,history) end-1] == "skip"} {
+	    end_game $gid 0.5 [::msgcat::mc "Draw (Both players skipped move)"]
+	}
+	update_controls $gid $draw
+	draw_position $gid
+	highlight_last_move $gid
+
+	return [list result [jlib::wrapper:createtag turn \
+				 -vars [list xmlns games:board \
+					     type $flags(game) \
+					     id $flags(id)]]]
+    } else {
+	return [list error modify not-acceptable]
+    }
+}
+
+
+###############################################################################
+
+proc renju::calc_moves {} {
+    variable moves
+
+    for {set c 0} {$c < 15} {incr c} {
+	for {set r 0} {$r < 15} {incr r} {
+	    for {set moves(d1,$c,$r) {}; set x [expr {$c+1}]; set y [expr {$r+1}]} \
+		{($x < 15) && ($y < 15)} {incr x; incr y} {
+		lappend moves(d1,$c,$r) $x $y
+	    }
+	    for {set moves(d2,$c,$r) {}; set x [expr {$c-1}]; set y [expr {$r+1}]} \
+		{($x >= 0) && ($y < 15)} {incr x -1; incr y} {
+		lappend moves(d2,$c,$r) $x $y
+	    }
+	    for {set moves(d3,$c,$r) {}; set x [expr {$c-1}]; set y [expr {$r-1}]} \
+		{($x >= 0) && ($y >= 0)} {incr x -1; incr y -1} {
+		lappend moves(d3,$c,$r) $x $y
+	    }
+	    for {set moves(d4,$c,$r) {}; set x [expr {$c+1}]; set y [expr {$r-1}]} \
+		{($x < 15) && ($y >= 0)} {incr x; incr y -1} {
+		lappend moves(d4,$c,$r) $x $y
+	    }
+	    for {set moves(h1,$c,$r) {}; set x [expr {$c+1}]} {$x < 15} {incr x} {
+		lappend moves(h1,$c,$r) $x $r
+	    }
+	    for {set moves(h2,$c,$r) {}; set x [expr {$c-1}]} {$x >= 0} {incr x -1} {
+		lappend moves(h2,$c,$r) $x $r
+	    }
+	    for {set moves(v1,$c,$r) {}; set y [expr {$r+1}]} {$y < 15} {incr y} {
+		lappend moves(v1,$c,$r) $c $y
+	    }
+	    for {set moves(v2,$c,$r) {}; set y [expr {$r-1}]} {$y >= 0} {incr y -1} {
+		lappend moves(v2,$c,$r) $c $y
+	    }
+	}
+    }
+}
+
+hook::add finload_hook [namespace current]::renju::calc_moves 100
+
+proc renju::center {c r} {
+    variable square_size
+    variable line_width
+
+    set r [expr {14 - $r}]
+    list [expr {$line_width + ($square_size * 0.5) + \
+		    (($square_size + $line_width) * $c)}] \
+	[expr {$line_width + ($square_size * 0.5) + \
+		   (($square_size + $line_width) * $r)}]
+}
+
+proc renju::close {gid} {
+    variable $gid
+    upvar 0 $gid flags
+
+    array unset flags
+}
+
+proc renju::exists {gid} {
+    variable $gid
+    info exists $gid
+}
+
+proc renju::open {gid} {
+    global font
+    variable options
+    variable game_names
+    variable square_size
+    variable line_width
+    variable $gid
+    upvar 0 $gid flags
+
+    set jid $flags(opponent)
+
+    set w $flags(window)
+    if {[winfo exists $w]} {
+	return
+    }
+
+    set title [::msgcat::mc "%s with %s" $game_names($flags(game)) \
+			    [get_nick $flags(connid) $jid chat]]
+    add_win $w -title $title -tabtitle $title \
+	-class renju
+
+    set board [canvas $w.board \
+		   -width [expr {($square_size + $line_width) * 15}] \
+		   -height [expr {($square_size + $line_width) * 15}]]
+    pack $board -side left -anchor w -padx 10
+
+    set flags(board) $board
+
+    set flags(show_last_move) $options(show_last_move)
+    set relief [expr {$flags(show_last_move) ? "sunken" : "raised"}]
+    set slm [Button $w.show_last_move -text [::msgcat::mc "Show last move"] \
+		-relief $relief \
+		-command [list [namespace current]::toggle_show_last_move $gid]]
+    pack $slm -side top -anchor w -fill x
+    set flags(show_last_move_button) $slm
+
+    frame $w.move 
+    pack $w.move -side top -anchor w
+    label $w.move.title -text [::msgcat::mc "Move: "]
+    pack $w.move.title -side left
+    label $w.move.on_move -anchor w \
+	-textvariable [namespace current]::${gid}(move_label)
+    pack $w.move.on_move -side left -anchor w
+
+    set bbox [ButtonBox $w.bbox -orient vertical -spacing 0]
+    $bbox add -text [::msgcat::mc "Skip the move"] \
+	-command [list [namespace current]::send_skip $gid]
+    $bbox add -text [::msgcat::mc "Propose a draw"] \
+	-command [list [namespace current]::toggle_draw $gid]
+    $bbox add -text [::msgcat::mc "Accept the draw proposal"] \
+	-state disabled \
+	-command [list [namespace current]::accept_draw $gid]
+    $bbox add -text [::msgcat::mc "Resign the game"] \
+	-command [list [namespace current]::send_resign $gid]
+    grid columnconfigure $bbox 0 -weight 1
+    pack $bbox -side bottom -anchor w -fill x
+    set flags(bbox) $bbox
+    set_tooltips
+
+    #label $w.history -text [::msgcat::mc "History"]
+    #pack $w.history -side top -anchor w
+    set hsw [ScrolledWindow $w.hsw]
+    pack $hsw -side top -fill x -expand yes
+    set tabstop1 [font measure $font "99.."]
+    set tabstop2 [font measure $font "99..Qa8-a8+= "]
+    set ht [text $w.text -font $font -tabs "$tabstop1 $tabstop2" -wrap word \
+		 -height 60 -state disabled]
+    $ht tag configure attention -foreground [option get $ht errorForeground Text]
+    $hsw setwidget $ht
+    set flags(hw) $ht
+
+    set dsq_color #77a26d
+    set lsq_color #c8c365
+
+    for {set c 0} {$c < 15} {incr c} {
+	for {set r 0} {$r < 15} {incr r} {
+	    set x1 [expr {$line_width + (($square_size + $line_width) * $c)}]
+	    set x2 [expr {($square_size + $line_width) * ($c + 1)}]
+	    set y1 [expr {$line_width + (($square_size + $line_width) * $r)}]
+	    set y2 [expr {($square_size + $line_width) * ($r + 1)}]
+	    set color [expr {($c+$r) % 2 ? $dsq_color : $lsq_color}]
+	    if {$c == 0 && $r == 0} {
+		set img "ltf"
+	    } elseif {$c == 0 && $r == 14} {
+		set img "lbf"
+	    } elseif {$c == 0} {
+		set img "lf"
+	    } elseif {$c == 14 && $r == 0} {
+		set img "rtf"
+	    } elseif {$c == 14 && $r == 14} {
+		set img "rbf"
+	    } elseif {$c == 14} {
+		set img "rf"
+	    } elseif {$r == 0} {
+		set img "tf"
+	    } elseif {$r == 14} {
+		set img "bf"
+	    } elseif {$c == 7 && $r == 7} {
+		set img "cf"
+	    } else {
+		set img "mf"
+	    }
+
+	    $board create image $x1 $y1 -image renju/$img -anchor nw \
+		-tags [list background [list cr $c [expr {14-$r}]]]
+	    $board create rectangle $x1 $y1 $x2 $y2 \
+		-outline {} \
+		-tags [list last [list cr $c [expr {14-$r}]]]
+	    $board create rectangle $x1 $y1 $x2 $y2 \
+		-outline {} \
+		-tags [list square [list cr $c [expr {14-$r}]]]
+	}
+    }
+
+    bind $board <Any-Enter> \
+	[list [namespace current]::motion $gid %x %y]
+    bind $board <Any-Motion> \
+	[list [namespace current]::motion $gid %x %y]
+    bind $board <Any-Leave> \
+	[list [namespace current]::leave $gid %x %y]
+    bind $board <ButtonRelease-1> \
+	[list [namespace current]::release $gid %x %y]
+
+    bind $w <Destroy> [list [namespace current]::close $gid]
+
+    make_default_position $gid
+    
+    draw_position $gid
+    update_controls $gid
+    find_legal_moves $gid $flags(position,turn)
+}
+
+proc renju::set_tooltips {args} {
+    variable options
+
+    if {$options(show_tooltips)} {
+	set tooltip0 [::msgcat::mc "Press button if you want skip current move"]
+	set tooltip1 [::msgcat::mc "Press button and make move if you want propose draw"]
+	set tooltip2 [::msgcat::mc "Press button if you want accept the draw proposal"]
+	set tooltip3 [::msgcat::mc "Press button if you want resign"]
+    } else {
+	set tooltip0 ""
+	set tooltip1 ""
+	set tooltip2 ""
+	set tooltip3 ""
+    }
+
+    foreach var [info vars [namespace current]::*] {
+	upvar 0 $var flags
+	if {[info exists flags(bbox)]} {
+	    catch {
+		$flags(bbox) itemconfigure 0 -helptext $tooltip0
+		$flags(bbox) itemconfigure 1 -helptext $tooltip1
+		$flags(bbox) itemconfigure 2 -helptext $tooltip2
+		$flags(bbox) itemconfigure 3 -helptext $tooltip3
+	    }
+	}
+    }
+}
+
+proc renju::toggle_show_last_move {gid} {
+    variable $gid
+    upvar 0 $gid flags
+
+    set flags(show_last_move) [expr {!$flags(show_last_move)}]
+
+    set relief [expr {$flags(show_last_move) ? "sunken" : "raised"}]
+    $flags(show_last_move_button) configure -relief $relief
+
+    highlight_last_move $gid
+}
+
+proc renju::toggle_draw {gid} {
+    variable $gid
+    upvar 0 $gid flags
+
+    set flags(position,draw) [expr {!$flags(position,draw)}]
+
+    if {$flags(position,draw)} {
+	$flags(bbox) itemconfigure 0 -relief sunken
+    } else {
+	$flags(bbox) itemconfigure 0 -relief raised
+    }
+}
+
+proc renju::update_controls {gid {draw_proposed 0}} {
+    variable $gid
+    upvar 0 $gid flags
+
+    $flags(bbox) itemconfigure 0 -relief raised
+
+    if {[is_my_move $gid]} {
+	$flags(board) config -cursor ""
+	set flags(position,draw) 0
+	if {$draw_proposed} {
+	    $flags(bbox) itemconfigure 0 -state disabled
+	    $flags(bbox) itemconfigure 1 -state disabled
+	    $flags(bbox) itemconfigure 2 -state normal
+	    $flags(bbox) itemconfigure 3 -state disabled
+	} else {
+	    $flags(bbox) itemconfigure 0 -state normal
+	    $flags(bbox) itemconfigure 1 -state normal
+	    $flags(bbox) itemconfigure 2 -state disabled
+	    $flags(bbox) itemconfigure 3 -state normal
+	}
+    } elseif {![is_white $flags(position,turn)] && \
+	      ![is_black $flags(position,turn)]} {
+	$flags(board) config -cursor ""
+	$flags(bbox) itemconfigure 0 -state disabled
+	$flags(bbox) itemconfigure 1 -state disabled
+	$flags(bbox) itemconfigure 2 -state disabled
+	$flags(bbox) itemconfigure 3 -state disabled
+    } else {
+	$flags(board) config -cursor watch
+	$flags(bbox) itemconfigure 0 -state disabled
+	$flags(bbox) itemconfigure 1 -state disabled
+	$flags(bbox) itemconfigure 2 -state disabled
+	$flags(bbox) itemconfigure 3 -state disabled
+    }
+}
+
+proc renju::end_game {gid my_score message} {
+    variable $gid
+    upvar 0 $gid flags
+
+    set opponent_score [expr {1 - $my_score}]
+
+    if {[is_black $flags(our_color)]} {
+	set score "$my_score : $opponent_score"
+    } else {
+	set score "$opponent_score : $my_score"
+    }
+
+    set flags(position,turn) none
+    set flags(move_label) $message
+
+    set hw $flags(hw)
+    $hw configure -state normal
+    catch {$hw delete attention.first attention.last}
+    $hw delete {end -1 char} end
+    $hw insert end "\n\t\t$score\n"
+    $hw see end
+    $hw configure -state disabled
+}
+
+proc renju::draw_position {gid} {
+    variable $gid
+    upvar 0 $gid flags
+
+    $flags(board) delete figure
+
+    for {set c 0} {$c < 15} {incr c} {
+	for {set r 0} {$r < 15} {incr r} {
+	    if {$flags(position,$c,$r) != ""} {
+		$flags(board) create image [center $c $r] \
+		    -image renju/$flags(position,$c,$r) \
+		    -tags [list figure $flags(position,$c,$r) [list cr $c $r]]
+	    }
+	}
+    }
+}
+
+proc renju::motion {gid x y} {
+    variable $gid
+    upvar 0 $gid flags
+
+    set board $flags(board)
+
+    set x [$board canvasx $x]
+    set y [$board canvasy $y]
+
+    $board itemconfigure dst_sq&&square -outline ""
+    $board dtag dst_sq
+    
+    $board addtag dst_sq overlapping $x $y $x $y
+    set tags [$board gettags dst_sq&&background]
+    lassign [lindex $tags [lsearch $tags cr*]] cr c r
+    $board addtag dst_sq withtag [list cr $c $r]&&square
+    
+    if {[info exists flags(position,$c,$r)] && $flags(position,$c,$r) == ""} {
+	$board itemconfigure dst_sq&&square -outline red
+	$board itemconfigure dst_sq&&legal&&square -outline blue
+    }
+}
+
+proc renju::leave {gid x y} {
+    variable $gid
+    upvar 0 $gid flags
+
+    set board $flags(board)
+
+    $board itemconfigure dst_sq&&square -outline ""
+    $board dtag dst_sq
+    highlight_last_move $gid
+}
+
+proc renju::release {gid x y} {
+    variable options
+    variable $gid
+    upvar 0 $gid flags
+
+    set board $flags(board)
+
+    set x [$board canvasx $x]
+    set y [$board canvasy $y]
+    $board dtag dst_sq
+    $board addtag dst_sq overlapping $x $y $x $y
+
+    set tags [$board gettags dst_sq&&background]
+    lassign [lindex $tags [lsearch $tags cr*]] cr c r
+    $board dtag dst_sq
+
+    if {$options(allow_illegal) || [is_my_move $gid]} {
+	if {[do_move $gid $c $r $flags(position,draw)]} {
+	    $board itemconfigure [list cr $c $r]&&square -outline ""
+	}
+    }
+    
+    update_controls $gid
+    draw_position $gid
+
+    highlight_last_move $gid
+}
+
+proc renju::highlight_last_move {gid} {
+    variable $gid
+    upvar 0 $gid flags
+
+    $flags(board) itemconfigure last -outline ""
+    
+    if {[catch {lassign $flags(position,last_move) ct rt}]} {
+	return
+    }
+
+    if {$flags(show_last_move)} {
+	set color white
+    } else {
+	set color {}
+    }
+
+    $flags(board) itemconfigure [list cr $ct $rt]&&last -outline $color
+}
+
+proc renju::do_move {gid ct rt draw} {
+    variable options
+    variable moves
+    variable $gid
+    upvar 0 $gid flags
+    
+    if {$ct == "" || $rt == ""} {
+	return 0
+    }
+
+    set my_move [is_my_move $gid]
+
+    if {![is_move_legal $gid $ct $rt]} {
+	if {$my_move && !$options(allow_illegal)} {
+	    return 0
+	}
+	if {!$my_move && !$options(accept_illegal)} {
+	    return 0
+	}
+    }
+
+    save_position $gid
+
+    if {[is_white $flags(position,turn)]} {
+	set mover w
+	set opp b
+    } else {
+	set mover b
+	set opp w
+    }
+
+    set flags(position,$ct,$rt) $mover
+    set flags(position,last_move) [list $ct $rt]
+
+    set checkmate [test_checkmate $gid $ct $rt]
+
+    if {[is_white $flags(position,turn)]} {
+	find_legal_moves $gid black
+    } else {
+	find_legal_moves $gid white
+    }
+    set skip [lempty $flags(legal_moves)]
+
+    add_move_to_history $gid $ct $rt
+
+    if {!$checkmate && $draw && !$my_move} {
+	attention_message $gid \
+	    [::msgcat::mc "\n\n Opponent proposes a draw\n\n"]
+    }
+
+    if {$my_move} {
+	send_move $gid $ct $rt
+    }
+
+    if {!$skip} {
+	if {[is_white $flags(position,turn)]} {
+	    set flags(position,turn) black
+	} else {
+	    set flags(position,turn) white
+	}
+    }
+
+    find_legal_moves $gid $flags(position,turn)
+
+    set endgame 0
+    if {$skip && [lempty $flags(legal_moves)]} {
+	# Can't find any move for both sides
+	set endgame 1
+    } elseif {$skip} {
+	add_move_to_history $gid
+    }
+	
+    if {$checkmate} {
+	if {$my_move} {
+	    # I win
+	    end_game $gid 1 [::msgcat::mc "You win"]
+	} else {
+	    # Opponent wins
+	    end_game $gid 0 [::msgcat::mc "Opponent wins"]
+	}
+    } elseif {$endgame} {
+	# Draw
+	end_game $gid 0.5 [::msgcat::mc "Draw"]
+    }
+
+    tab_set_updated [winfo parent $flags(board)] 1 mesg_to_user
+    return 1
+}
+
+proc renju::accept_draw {gid} {
+    variable $gid
+    upvar 0 $gid flags
+
+    jlib::send_iq set \
+	[jlib::wrapper:createtag turn \
+	     -vars [list xmlns games:board \
+			type $flags(game) \
+			id $flags(id)] \
+	     -subtags [list [jlib::wrapper:createtag accept]]] \
+	-to $flags(opponent) \
+	-connection $flags(connid)
+
+	end_game $gid 0.5 [::msgcat::mc "Draw (You accepted)"]
+	update_controls $gid
+	draw_position $gid
+	highlight_last_move $gid
+}
+
+proc renju::send_skip {gid} {
+    variable $gid
+    upvar 0 $gid flags
+
+    add_move_to_history $gid
+    if {[is_white $flags(position,turn)]} {
+	set flags(position,turn) black
+    } else {
+	set flags(position,turn) white
+    }
+
+    jlib::send_iq set \
+	[jlib::wrapper:createtag turn \
+	     -vars [list xmlns games:board \
+			type $flags(game) \
+			id $flags(id)] \
+	     -subtags [list [jlib::wrapper:createtag skip]]] \
+	-to $flags(opponent) \
+	-connection $flags(connid) \
+	-command [list [namespace current]::send_result $gid]
+
+    if {[lindex $flags(position,history) end] == "skip" && \
+	    [lindex $flags(position,history) end-1] == "skip"} {
+	end_game $gid 0.5 [::msgcat::mc "Draw (Both players skipped move)"]
+    }
+    update_controls $gid
+    draw_position $gid
+    highlight_last_move $gid
+}
+
+proc renju::send_resign {gid} {
+    variable $gid
+    upvar 0 $gid flags
+
+    jlib::send_iq set \
+	[jlib::wrapper:createtag turn \
+	     -vars [list xmlns games:board \
+			type $flags(game) \
+			id $flags(id)] \
+	     -subtags [list [jlib::wrapper:createtag resign]]] \
+	-to $flags(opponent) \
+	-connection $flags(connid)
+
+    end_game $gid 0 [::msgcat::mc "Opponent wins (You resigned)"]
+    update_controls $gid
+    draw_position $gid
+    highlight_last_move $gid
+}
+
+proc renju::send_move {gid ct rt} {
+    variable $gid
+    upvar 0 $gid flags
+
+    set put_tags [list [jlib::wrapper:createtag put -vars [list pos "$ct,$rt"]]]
+    if {$flags(position,draw)} {
+	lappend put_tags [jlib::wrapper:createtag draw]
+    }
+
+    jlib::send_iq set \
+	[jlib::wrapper:createtag turn \
+	     -vars [list xmlns games:board \
+			type $flags(game) \
+			id $flags(id)] \
+	     -subtags $put_tags] \
+	-to $flags(opponent) \
+	-connection $flags(connid) \
+	-command [list [namespace current]::send_result $gid]
+}
+
+proc renju::send_result {gid res child} {
+    if {$res != "OK"} {
+	attention_message $gid \
+	    [format [::msgcat::mc "\n\n Opponent rejected move:\n %s\n\n"] \
+		[error_to_string $child]]
+	restore_position $gid
+    }
+}
+
+proc renju::count_pieces {gid} {
+    variable $gid
+    upvar 0 $gid flags
+
+    set b 0
+    set w 0
+    for {set ct 0} {$ct < 15} {incr ct} {
+	for {set rt 0} {$rt < 15} {incr rt} {
+	    switch -- $flags(position,$ct,$rt) {
+		b { incr b }
+		w { incr w }
+	    }
+	}
+    }
+    return [list $b $w]
+}
+
+proc renju::add_move_to_history {gid {ct ""} {rt ""}} {
+    variable $gid
+    upvar 0 $gid flags
+
+    incr flags(position,halfmove)
+
+    if {$ct != "" && $rt != ""} {
+	lappend flags(position,history) [list $ct $rt]
+    } else {
+	lappend flags(position,history) skip
+    }
+
+    set hw $flags(hw)
+    $hw configure -state normal
+    $hw delete 0.0 end
+
+    $hw insert end "\t[::msgcat::mc Black]\t[::msgcat::mc White]\n"
+    set i 1
+    foreach {b w} $flags(position,history) {
+	$hw insert end "${i}.\t"
+	if {$b == "skip"} {
+	    $hw insert end "--\t"
+	} elseif {$b != {}} {
+	    lassign $b ct rt
+	    incr rt
+	    set lt [format %c [expr {$ct+97}]]
+	    $hw insert end "$lt$rt\t"
+	}
+	if {$w == "skip"} {
+	    $hw insert end "--\n"
+	} elseif {$w != {}} {
+	    lassign $w ct rt
+	    incr rt
+	    set lt [format %c [expr {$ct+97}]]
+	    $hw insert end "$lt$rt\n"
+	} else {
+	    $hw insert end "\n"
+	}
+	incr i
+    }
+    $hw see end
+    $hw configure -state disabled
+}
+
+proc renju::test_checkmate {gid ct rt} {
+    variable $gid
+    upvar 0 $gid flags
+    variable moves
+
+    if {$flags(position,$ct,$rt) == ""} {
+	return 0
+    }
+    set mover $flags(position,$ct,$rt)
+
+    foreach dir {d1 d2 d3 d4 h1 h2 v1 v2} {
+	set str($dir) 0
+	foreach {x y} $moves($dir,$ct,$rt) {
+	    if {$flags(position,$x,$y) == $mover} {
+		incr str($dir)
+	    } else {
+		break
+	    }
+	}
+    }
+    set s1 [expr {1 + $str(d1) + $str(d3)}]
+    set s2 [expr {1 + $str(d2) + $str(d4)}]
+    set s3 [expr {1 + $str(h1) + $str(h2)}]
+    set s4 [expr {1 + $str(v1) + $str(v2)}]
+
+    switch -- $flags(game) {
+	gomoku:freestyle {
+	    return [expr {$s1 >= 5 || $s2 >= 5 || $s3 >= 5 || $s4 >= 5}]
+	}
+	gomoku:standard {
+	}
+	renju {
+	}
+    }
+}
+
+proc renju::find_legal_moves {gid color} {
+    variable $gid
+    upvar 0 $gid flags
+
+    set flags(legal_moves) {}
+
+    for {set ct 0} {$ct < 15} {incr ct} {
+	for {set rt 0} {$rt < 15} {incr rt} {
+	    if {$flags(position,$ct,$rt) != "" && \
+		    [test_checkmate $gid $ct $rt]} {
+		highlight_legal_moves $gid
+		return
+	    }
+	}
+    }
+
+    for {set ct 0} {$ct < 15} {incr ct} {
+	for {set rt 0} {$rt < 15} {incr rt} {
+	    if {$flags(position,$ct,$rt) == "" && \
+		    [check_legal $gid $ct $rt $color]} {
+		lappend flags(legal_moves) [list $ct $rt]
+	    }
+	}
+    }
+    highlight_legal_moves $gid
+}
+
+proc renju::check_legal {gid ct rt color} {
+    variable moves
+    variable $gid
+    upvar 0 $gid flags
+
+    set me [expr {[is_black $color] ? "b" : "w"}]
+    set opp [expr {[is_black $color] ? "w" : "b"}]
+
+    switch -- $flags(game) {
+	gomoku:freestyle {
+	    return 1
+	}
+	gomoku:standard {
+	    return 1
+	}
+	renju {
+	    if {![is_black $color]} {
+		return 1
+	    } else {
+		set empty 1
+		for {set c 0} {$c < 15} {incr c} {
+		    for {set r 0} {$r < 15} {incr r} {
+			if {$flags(position,$c,$r) != ""} {
+			    set empty 0
+			}
+		    }
+		}
+		if {$empty} {
+		    if {$ct == 7 && $rt == 7} {
+			return 1
+		    } else {
+			return 0
+		    }
+		} else {
+		    # TODO: Fouls
+		    return 1
+		}
+	    }
+	}
+    }
+}
+
+proc renju::is_move_legal {gid ct rt} {
+    variable $gid
+    upvar 0 $gid flags
+
+    expr {[lmatch -regexp $flags(legal_moves) ^[list $ct $rt]] != {}}
+}
+
+
+proc renju::highlight_legal_moves {gid} {
+    variable $gid
+    upvar 0 $gid flags
+
+    set board $flags(board)
+
+    $board dtag legal
+    foreach move $flags(legal_moves) {
+	lassign $move ct rt
+	$board addtag legal withtag [list cr $ct $rt]&&square
+
+    }
+}
+
+proc renju::attention_message {gid message} {
+    variable $gid
+    upvar 0 $gid flags
+
+    set hw $flags(hw)
+    $hw configure -state normal
+    $hw delete {end -1 char} end
+    $hw insert end $message attention
+    $hw see end
+    $hw configure -state disabled
+}
+
+proc renju::is_my_move {gid} {
+    variable $gid
+    upvar 0 $gid flags
+    is_same_color $flags(position,turn) $flags(our_color)
+}
+
+proc renju::is_white {f} {
+    string equal -length 1 $f w
+}
+
+proc renju::is_black {f} {
+    string equal -length 1 $f b
+}
+
+proc renju::is_same_color {f1 f2} {
+    string equal -length 1 $f1 $f2
+}
+
+proc renju::add_groupchat_user_menu_item {m connid jid} {
+    set mm $m.gamesmenu
+    if {![winfo exists $mm]} {
+	menu $mm -tearoff 0
+	$m add cascade -label [::msgcat::mc "Games"] -menu $mm
+    }
+    $mm add command -label [::msgcat::mc "Gomoku/Renju..."] \
+	-command [list [namespace current]::invite_dialog $connid $jid]
+}
+
+hook::add roster_create_groupchat_user_menu_hook \
+    [namespace current]::renju::add_groupchat_user_menu_item 51
+hook::add chat_create_user_menu_hook \
+    [namespace current]::renju::add_groupchat_user_menu_item 51
+hook::add roster_jid_popup_menu_hook \
+    [namespace current]::renju::add_groupchat_user_menu_item 51
+
+proc renju::iq_create {varname connid from child} {
+    upvar 2 $varname var
+
+    jlib::wrapper:splitxml $child tag vars isempty chdata children
+
+    set game [jlib::wrapper:getattr $vars type]
+
+    switch -- $game {
+	gomoku:freestyle -
+	gomoku:standard -
+	renju {}
+	default {
+	    return
+	}
+    }
+
+    if {[jlib::wrapper:isattr $vars color]} {
+	set color [jlib::wrapper:getattr $vars color]
+	switch -- $color {
+	    white -
+	    black { }
+	    default {
+		set var [list error modify bad-request]
+	    }
+	}
+    } else {
+	set color white
+    }
+    set var [[namespace current]::invited_dialog \
+		 $game $connid $from \
+		 [jlib::wrapper:getattr $vars id] \
+		 $color]
+}
+
+hook::add games_board_create_hook [namespace current]::renju::iq_create
+
+proc renju::iq_turn {varname connid from child} {
+    upvar 2 $varname var
+
+    jlib::wrapper:splitxml $child tag vars isempty chdata children
+
+    switch -- [jlib::wrapper:getattr $vars type] {
+	gomoku:freestyle -
+	gomoku:standard -
+	renju {}
+	default {
+	    return
+	}
+    }
+
+    set gid [make_gid $from [jlib::wrapper:getattr $vars id]]
+    if {[exists $gid]} {
+	set var [[namespace current]::turn_recv $gid $children]
+    } else {
+	set var [list error cancel item-not-found]
+    }
+}
+
+hook::add games_board_turn_hook [namespace current]::renju::iq_turn
+
+
+# Common games:board part
+proc iq_games_board_create {connid from lang child} {
+    set res [list error cancel feature-not-implemented]
+    hook::run games_board_create_hook res $connid $from $child
+    return $res
+}
+
+iq::register_handler set create games:board \
+    [namespace current]::iq_games_board_create
+
+proc iq_games_board_turn {connid from lang child} {
+    set res [list error cancel feature-not-implemented]
+    hook::run games_board_turn_hook res $connid $from $child
+    return $res
+}
+
+iq::register_handler set turn games:board \
+    [namespace current]::iq_games_board_turn
+


Property changes on: trunk/tkabber-plugins/renju/renju.tcl
___________________________________________________________________
Name: svn:keywords
   + Author Date Id Revision
Name: svn:eol-style
   + native



More information about the Tkabber-dev mailing list