(define "Columns" ) (define "PlayableSites" (sites (values Remembered "Playable"))) (define "NextHole" ("NextSiteOnTrack" #3 from:#1 #2)) (define "NoPieceOnBoard" (no Pieces All in:(union (sites Top) (sites Bottom)))) (define "OnlySingleCounters" (all Sites (union (sites Top) (sites Bottom)) if:(>= 1 (count at:(site))))) (define "NumCapture" (+ (count at:(mapEntry #1)) (count in:(sites #1)))) (define "OriginalNumberCounters" (* 6 (count Sites in:(union (sites Top) (sites Bottom))))) //------------------------------------------------------------------------------ (game "Kiuthi" (players 2) (equipment { (mancalaBoard 2 "Columns" { (track "TrackCCW" "1,E,N,W" loop:True) (track "TrackCW" loop:True) } ) (piece "Seed" Shared) (regions P1 (sites Bottom)) // P1 home (regions P2 (sites Top)) // P2 home (map {(pair P1 FirstSite) (pair P2 LastSite)}) // kalahs (storage pits) }) (rules (start { (set Count 6 to:(sites Track)) (set RememberValue "Playable" (union (sites Top) (sites Bottom))) }) phases:{ (phase "Sowing" (play (or (if (!= 2 (var "Direction")) (move Select (from (if ("SameTurn") (if (= 1 (var "ReachOpponent")) (sites {(var "Replay")}) (sites Mover) ) (sites Mover) ) if:(and (< 1 (count at:(from))) (is In (from) ("PlayableSites"))) ) (then (if (!= 1 (var "OppositeDirection")) (sow "TrackCCW" apply:(if (!= 1 (var "ReachOpponent")) (if (is In (to) (sites Next)) (if (< 1 (count at:(to))) (and { (moveAgain) (set Var "Replay" (to)) (set Var "ReachOpponent" 1) (if (= 1 (var "OppositeDirection")) (set Var "OppositeDirection" 0) (set Var "OppositeDirection" 1) ) (set Var "Direction" 1) }) (and (set Var "ReachOpponent" 0) (set Var "Direction" 0) ) ) (moveAgain) ) (if (< 1 (count at:(to))) (and { (moveAgain) (set Var "Replay" (to)) (if (= 1 (var "OppositeDirection")) (set Var "OppositeDirection" 0) (set Var "OppositeDirection" 1) ) (set Var "Direction" 1) }) (and { (set Var "ReachOpponent" 0) (set Var "OppositeDirection" 0) (set Var "Direction" 0) (if (is In (to) (sites Mover)) (if (is Occupied ("OppositePit" (to))) (and (fromTo (from ("OppositePit" (to))) (to (mapEntry Mover)) count:(count at:("OppositePit" (to))) ) (if (and (is Empty ("NextHole" (to) "TrackCCW" 1)) (is In ("NextHole" (to) "TrackCCW" 1) (sites Mover))) (if (is Occupied ("OppositePit" ("NextHole" (to) "TrackCCW" 1))) (fromTo (from ("OppositePit" ("NextHole" (to) "TrackCCW" 1))) (to (mapEntry Mover)) count:(count at:("OppositePit" ("NextHole" (to) "TrackCCW" 1))) ) ) ) ) ) ) }) ) ) skipIf:(not (is In (to) ("PlayableSites"))) ) (sow "TrackCW" apply:(if (!= 1 (var "ReachOpponent")) (if (is In (to) (sites Next)) (if (< 1 (count at:(to))) (and { (moveAgain) (set Var "Replay" (to)) (set Var "ReachOpponent" 1) (if (= 1 (var "OppositeDirection")) (set Var "OppositeDirection" 0) (set Var "OppositeDirection" 1) ) (set Var "Direction" 1) }) (and (set Var "ReachOpponent" 0) (set Var "Direction" 0) ) ) (moveAgain) ) (if (< 1 (count at:(to))) (and { (moveAgain) (set Var "Replay" (to)) (if (= 1 (var "OppositeDirection")) (set Var "OppositeDirection" 0) (set Var "OppositeDirection" 1) ) (set Var "Direction" 1) }) (and { (set Var "ReachOpponent" 0) (set Var "OppositeDirection" 0) (set Var "Direction" 0) (if (is In (to) (sites Mover)) (if (is Occupied ("OppositePit" (to))) (and (fromTo (from ("OppositePit" (to))) (to (mapEntry Mover)) count:(count at:("OppositePit" (to))) ) (if (and (is Empty ("NextHole" (to) "TrackCW" 1)) (is In ("NextHole" (to) "TrackCW" 1) (sites Mover))) (if (is Occupied ("OppositePit" ("NextHole" (to) "TrackCW" 1))) (fromTo (from ("OppositePit" ("NextHole" (to) "TrackCW" 1))) (to (mapEntry Mover)) count:(count at:("OppositePit" ("NextHole" (to) "TrackCW" 1))) ) ) ) ) ) ) }) ) ) skipIf:(not (is In (to) ("PlayableSites"))) ) ) ) ) ) (if (!= 1 (var "Direction")) (move Select (from (if ("SameTurn") (if (= 1 (var "ReachOpponent")) (sites {(var "Replay")}) (sites Mover) ) (sites Mover) ) if:(and (< 1 (count at:(from))) (is In (from) ("PlayableSites"))) ) (then (if (!= 1 (var "OppositeDirection")) (sow "TrackCW" apply:(if (!= 1 (var "ReachOpponent")) (if (is In (to) (sites Next)) (if (< 1 (count at:(to))) (and { (moveAgain) (set Var "Replay" (to)) (set Var "ReachOpponent" 1) (if (= 1 (var "OppositeDirection")) (set Var "OppositeDirection" 0) (set Var "OppositeDirection" 1) ) (set Var "Direction" 2) }) (and (set Var "ReachOpponent" 0) (set Var "Direction" 0) ) ) (moveAgain) ) (if (< 1 (count at:(to))) (and { (moveAgain) (set Var "Replay" (to)) (if (= 1 (var "OppositeDirection")) (set Var "OppositeDirection" 0) (set Var "OppositeDirection" 1) ) (set Var "Direction" 2) }) (and { (set Var "ReachOpponent" 0) (set Var "OppositeDirection" 0) (set Var "Direction" 0) (if (is In (to) (sites Mover)) (if (is Occupied ("OppositePit" (to))) (and (fromTo (from ("OppositePit" (to))) (to (mapEntry Mover)) count:(count at:("OppositePit" (to))) ) (if (and (is Empty ("NextHole" (to) "TrackCW" 1)) (is In ("NextHole" (to) "TrackCW" 1) (sites Mover))) (if (is Occupied ("OppositePit" ("NextHole" (to) "TrackCW" 1))) (fromTo (from ("OppositePit" ("NextHole" (to) "TrackCW" 1))) (to (mapEntry Mover)) count:(count at:("OppositePit" ("NextHole" (to) "TrackCW" 1))) ) ) ) ) ) ) }) ) ) skipIf:(not (is In (to) ("PlayableSites"))) ) (sow "TrackCCW" apply:(if (!= 1 (var "ReachOpponent")) (if (is In (to) (sites Next)) (if (< 1 (count at:(to))) (and { (moveAgain) (set Var "Replay" (to)) (set Var "ReachOpponent" 1) (if (= 1 (var "OppositeDirection")) (set Var "OppositeDirection" 0) (set Var "OppositeDirection" 1) ) (set Var "Direction" 2) }) (and (set Var "ReachOpponent" 0) (set Var "Direction" 0) ) ) (moveAgain) ) (if (< 1 (count at:(to))) (and { (moveAgain) (set Var "Replay" (to)) (if (= 1 (var "OppositeDirection")) (set Var "OppositeDirection" 0) (set Var "OppositeDirection" 1) ) (set Var "Direction" 2) }) (and { (set Var "ReachOpponent" 0) (set Var "OppositeDirection" 0) (set Var "Direction" 0) (if (is In (to) (sites Mover)) (if (is Occupied ("OppositePit" (to))) (and (fromTo (from ("OppositePit" (to))) (to (mapEntry Mover)) count:(count at:("OppositePit" (to))) ) (if (and (is Empty ("NextHole" (to) "TrackCCW" 1)) (is In ("NextHole" (to) "TrackCCW" 1) (sites Mover))) (if (is Occupied ("OppositePit" ("NextHole" (to) "TrackCCW" 1))) (fromTo (from ("OppositePit" ("NextHole" (to) "TrackCCW" 1))) (to (mapEntry Mover)) count:(count at:("OppositePit" ("NextHole" (to) "TrackCCW" 1))) ) ) ) ) ) ) }) ) ) skipIf:(not (is In (to) ("PlayableSites"))) ) ) ) ) ) (then (if ("OnlySingleCounters") (and { (forEach Site (sites P1) (fromTo (from (site)) (to (mapEntry P1)) count:(count at:(site)) ) ) (forEach Site (sites P2) (fromTo (from (site)) (to (mapEntry P2)) count:(count at:(site)) ) ) (forget Value "Playable" All) (if (< ("NumCapture" P1) ("NumCapture" P2)) (and (set NextPlayer (player 1)) (if (< ("NumCapture" P1) (/ ("OriginalNumberCounters") 8)) (forEach Site (difference (union (sites Top) (sites Bottom)) (expand (sites Right) steps:3)) (remember Value "Playable" (site)) ) (if (< ("NumCapture" P1) (/ ("OriginalNumberCounters") 4)) (forEach Site (difference (union (sites Top) (sites Bottom)) (expand (sites Right) steps:2)) (remember Value "Playable" (site)) ) (if (< ("NumCapture" P1) (/ ("OriginalNumberCounters") 2)) (forEach Site (difference (union (sites Top) (sites Bottom)) (expand (sites Right))) (remember Value "Playable" (site)) ) (forEach Site (union (sites Top) (sites Bottom)) (remember Value "Playable" (site)) ) ) ) ) ) (and (set NextPlayer (player 2)) (if (< ("NumCapture" P2) (/ ("OriginalNumberCounters") 8)) (forEach Site (difference (union (sites Top) (sites Bottom)) (expand (sites Right) steps:3)) (remember Value "Playable" (site)) ) (if (< ("NumCapture" P2) (/ ("OriginalNumberCounters") 4)) (forEach Site (difference (union (sites Top) (sites Bottom)) (expand (sites Right) steps:2)) (remember Value "Playable" (site)) ) (if (< ("NumCapture" P2) (/ ("OriginalNumberCounters") 2)) (forEach Site (difference (union (sites Top) (sites Bottom)) (expand (sites Right))) (remember Value "Playable" (site)) ) (forEach Site (union (sites Top) (sites Bottom)) (remember Value "Playable" (site)) ) ) ) ) ) ) }) ) ) ) ) (end (if ("NoPieceOnBoard") { (if (= 0 (count at:(mapEntry P1))) (result P2 Win)) (if (= 0 (count at:(mapEntry P2))) (result P1 Win)) (if (= (count at:(mapEntry P1)) (count at:(mapEntry P2))) (result Mover Draw)) } ) ) (nextPhase ("NoPieceOnBoard") "BetweenRounds") ) (phase "BetweenRounds" (play (if (is Occupied (mapEntry Mover)) (forEach Value min:1 max:(count at:(mapEntry Mover)) (move (from (mapEntry Mover)) (to (sites Mover) if:(is In (to) ("PlayableSites"))) count:(value) ) ) (then (if (is Occupied (mapEntry Mover)) (moveAgain) (forEach Site (sites Mover) (if (is Occupied (site)) (fromTo (from (mapEntry Next)) (to ("OppositePit" (site))) count:(count at:(site)) ) ) ) ) ) ) ) (nextPhase (is Empty (mapEntry Mover)) "Sowing") ) } ) ) //------------------------------------------------------------------------------ (option "Board Size" args:{ } { (item "5" <5> <"5,W,N,E"> "Each player has 5 holes.") (item "6" <6> <"6,W,N,E"> "Each player has 6 holes.")* (item "7" <7> <"7,W,N,E"> "Each player has 7 holes.") (item "8" <8> <"8,W,N,E"> "Each player has 8 holes.") (item "9" <9> <"9,W,N,E"> "Each player has 9 holes.") (item "10" <10> <"10,W,N,E"> "Each player has 10 holes.") }) //------------------------------------------------------------------------------ (metadata (info { (description "Kiuthi is a two-row mancala-style board game played by the Kikuyu people of Kenya. It was documented in the early twentieth century by Louis Leakey. ") (aliases {"Giuthi"}) (rules "2x5-10 holes (six is the most common), two stores on either end six counters in each hole. Play begins from any hole in a player's row, and counters can be sown in either direction. If sowing fails to reach a hole in the opponent's row, the player makes a second move from a loaded hole in the opposite direction, continuing to do so until they can sow in the opponent's row. If the last counter is sown in a loaded hole, the contents are lifted and sowing continues in the opposite direction. Play continues like this until the last counter lands in an empty hole. If the empty hole is in the opponent's row, play ends. If it lands in an empty hole in the player's row, the counters in the opponent's hole opposite it are placed in the store. If the next hole in the direction of sowing in the player's row is empty, the counters in the opponent's hole opposite that hole are also placed in the store. Single counters cannot be sown. Play continues until both players have only single counters in holes or all of the counters have been captured. These are then collected and placed in the store. If each player has the same number of counters, the game is a draw. If the number of counters is unequal, the player with fewer counters arranges them in their row however they wish. The opponent then matches this arrangement, placing any extras in the store. If the player has fewer than half of the original number of counters, the board is decreased in size by one hole. If less than a quarter, it is decreased by two holes, and if less than one eight, decreased by three holes. If in subsequent rounds a player regains more than one half/one quarter/one eighth of the original total, the board size is increased appropriately. Play continues until one player has all the counters.") (source "Leakey 1936: 165-173.") (id "245") (version "1.3.12") (classification "board/sow/two rows") (credit "Eric Piette") (origin "This game was played in Kenya, from around 1903 to 1972.") } ) (graphics { (board Style Mancala) }) (ai "Kiuthi_ai" ) )