I'm building 'Tori shogi' to learn Ludii grammar (obviously heavily recycling Ludii's shogi implementation). For some reason, I can't get the game the game to follow promotion rules (e.g. restricting how many pawns are allowed in a column). As far as I can tell, I'm using the same rule structure as the original shogi so I must be missing something pretty obvious.
Full file:
Any ideas what I might be missing?
Edit: swapped link for in-line code block
Full file:
Code:
// todo: triple pawn issue, icons, rule audit, mapping, QA, history, submission
// Methods pulled/modified from official 'Shogi' and 'Taikyoku shogi'
(define "CapturePiece"
(apply
(if (is Enemy (who at:(to)))
(add
(piece (mapEntry "Captured" (what at:(to))))
(to (mapEntry "Where" (what at:(to))))
)
)
)
)
(define "NextCannotMove"
(not (can Move (do (forEach Piece Next) ifAfterwards:(not ("IsInCheck" "lion" Next))) ))
)
// broken
(define "OnePawnPerColumn"
(=
(count Sites in:(sites Occupied by:Mover container:"Board" component:"duck"))
(count Sites in:(difference (sites Occupied by:Mover container:"Board" component:"duck") (sites Column (column of:(to)))))
)
)
//ditching
(define "PlaceSwallow"
(do
(move
(from (sites Occupied by:Mover container:"Hand" component:"duck"))
(to (difference (sites Empty) (sites Mover "LastRank"))
//if:"TwoSwallowPerColumn"
)
)
ifAfterwards:(not (and ("IsInCheck" "lion" Next) ("NextCannotMove")))
)
)
(define "InPromotionZone"
(is In #1 (sites Mover "Promotion"))
)
(define "InLastRank"
(is In #1 (sites Mover "LastRank"))
)
(define "InTwoFurthestRank"
(is In #1 (sites Mover "TwoFurthestRank"))
)
(define "Promote"
(move Promote (last To) (piece (mapEntry "Promoted" (what at:(last To)))))
)
(define "CanPromote"
(then
(if
(or ("InPromotionZone" (last To)) ("InPromotionZone" (last From)))
(moveAgain)
)
)
)
(define "SlideMove"
(move
Slide
#1
(to if:(is Enemy (who at:(to))) "CapturePiece")
#2
)
)
// This version allows you to define limits; needed for 'Eagle'
(define "SlideMoveAlt"
(move
Slide
#1
#2
(to if:(is Enemy (who at:(to)))
"CapturePiece")
#3
)
)
(define "StepMove"
(move
Step
#1
(to if:(or (is Empty (to)) (is Enemy (who at:(to)))) "CapturePiece")
#2
)
)
(define "HopMove"
(move
Hop
#1
(between
if:true
)
(to
if:(or (is Empty (to)) (is Enemy (who at:(to))))
(apply (if (is Enemy (who at:(to))) (remove (to))))
)
#2
)
)
//------------------------------------------------------------------------------
(game "Tori shogi"
(players {(player N) (player S)})
(equipment {
(board (square 7))
// Phoenix - King
(piece "lion" Each ("StepMove"))
// Swallow - Pawn
// (piece "duck" Each ("StepMove" (directions {Forward}) "CanPromote"))
(piece "duck" Each
("StepMove"
Forward
(then
(if ("InPromotionZone" (last To))
(if ("InLastRank" (last To))
("Promote")
(moveAgain)
)
)
)
)
)
// Goose (promoted Swallow)
(piece "goose" Each ("HopMove" (directions {FL FR Backward})))
// Falcon
(piece "fox" Each ("StepMove" (directions {BL Leftward FL Forward FR Rightward BL}) "CanPromote"))
// Eagle (promoted Falcon)
(piece "wolf" Each
(or {
("StepMove" (directions {Leftward Forward Rightward}))
("SlideMove" (directions {FL FR Backward}))
("SlideMoveAlt" (directions {BL BR}) (between (max 2)))
})
)
// Crane
(piece "snake" Each ("StepMove" (directions {BL FL Forward FR BL Backward})))
// Pheasant
(piece "jaguar" Each
(or
("StepMove" (directions {BL BR}))
("HopMove" (directions {Forward}))
)
)
// Quail, right
(piece "hex" Each
(or
("StepMove" (directions {BL}))
("SlideMove" (directions {Forward BR}))
)
)
// Quail, left
(piece "hexE" Each
(or
("StepMove" (directions {BR}))
("SlideMove" (directions {Forward BL}))
)
)
(regions "LastRankP1" P1 (sites Top))
(regions "LastRankP2" P2 (sites Bottom))
(regions "TwoFurthestRankP1" P1 (expand (sites Top)))
(regions "TwoFurthestRankP2" P2 (expand (sites Bottom)))
(regions "Promotion" P1 (expand (sites Top) steps:1))
(regions "Promotion" P2 (expand (sites Bottom) steps:1))
(map "Where" {
(pair (id "duck" P1) (handSite P2))
(pair (id "duck" P2) (handSite P1))
(pair (id "goose" P1) (handSite P2))
(pair (id "goose" P2) (handSite P1))
(pair (id "fox" P1) (handSite P2 1))
(pair (id "fox" P2) (handSite P1 1))
(pair (id "wolf" P1) (handSite P2 1))
(pair (id "wold" P2) (handSite P1 1))
(pair (id "snake" P1) (handSite P2 2))
(pair (id "snake" P2) (handSite P1 2))
(pair (id "jaguar" P1) (handSite P2 3))
(pair (id "jaguar" P2) (handSite P1 3))
(pair (id "hex" P1) (handSite P2 4))
(pair (id "hex" P2) (handSite P1 4))
(pair (id "hexE" P1) (handSite P2 5))
(pair (id "hexE" P2) (handSite P1 5))
})
(map "Captured" {
(pair (id "duck" P1) (id "duck" P2))
(pair (id "duck" P2) (id "duck" P1))
(pair (id "goose" P1) (id "duck" P2))
(pair (id "goose" P2) (id "duck" P1))
(pair (id "fox" P1) (id "fox" P2))
(pair (id "fox" P2) (id "fox" P1))
(pair (id "wolf" P1) (id "fox" P2))
(pair (id "wolf" P2) (id "fox" P1))
(pair (id "snake" P1) (id "snake" P2))
(pair (id "snake" P2) (id "snake" P1))
(pair (id "jaguar" P1) (id "jaguar" P2))
(pair (id "jaguar" P2) (id "jaguar" P1))
(pair (id "hex" P1) (id "hex" P2))
(pair (id "hex" P2) (id "hex" P1))
(pair (id "hexE" P1) (id "hexE" P2))
(pair (id "hexE" P2) (id "hexE" P1))
})
(map "Promoted" {
(pair (id "duck" P1) (id "goose" P1))
(pair (id "duck" P2) (id "goose" P2))
(pair (id "fox" P1) (id "wolf" P1))
(pair (id "fox" P2) (id "wolf" P2))
})
(hand Each size:7)
})
(rules
(start {
(place "fox1" coord:"D2")
(place "hexE1" coord:"A1")
(place "jaguar1" {"B1" "F1"})
(place "snake1" {"C1" "E1"})
(place "lion1" coord:"D1")
(place "hex1" coord:"G1")
(place "duck1" {"E4" "A3" "B3" "C3" "D3" "E3" "F3" "G3"})
(place "fox2" coord:"D6")
(place "hexE2" coord:"G7")
(place "jaguar2" {"B7" "F7"})
(place "snake2" {"C7" "E7"})
(place "lion2" coord:"D7")
(place "hex2" coord:"A7")
(place "duck2" {"C4" "A5" "B5" "C5" "D5" "E5" "F5" "G5"})
})
(play
(if ("SameTurn")
(or
("Promote")
(move Pass)
)
(do
(or {
(move
(from (sites Occupied by:Mover container:"Hand" components:{"duck" "fox" "snake" "jaguar" "hex" "hexE"}))
(to (sites Empty))
)
(do
(move
(from (sites Occupied by:Mover container:"Hand" component:"duck" ))
(to (difference (sites Empty) (sites Mover "Promotion") )
if:"OnePawnPerColumn"
)
)
ifAfterwards:(not (and ("IsInCheck" "lion" Next) ("NextCannotMove")))
)
(forEach Piece)
})
ifAfterwards:(not ("IsInCheck" "lion" Mover))
)
)
)
(end
{
(if (and
("IsInCheck" "lion" Next)
("NextCannotMove")
)
(result Mover Win)
)
(if (no Moves Next) (result Mover Win))
}
)
)
)
(metadata
(info
{
(description "Tori shogi ...")
(aliases {"Tori-Shogi"})
(rules "Pieces move as follows: Pheonix(1): moves one space in any direction ... Sparrow(9): moves one space forward. Pieces are promoted when reaching the opposite last two rows of the board. Pieces are captured when an opponent's piece moves to the space it occupies. Captured pieces are held and can re-enter the game under the control of the capturing player as their turn. The goal is to capture the other player's Pheonix.")
(source "NAMES YEAR: PAGES.")
(version "1.1.12")
(classification "board/war/shogi")
(credit "James Gimbi")
(origin "This game was played in COUNTRY, around YEAR.")
}
)
(graphics {
(player Colour P1 (colour White))
(player Colour P2 (colour White))
(board Style Shogi)
(piece Reflect P2 vertical:true horizontal:true)
(piece Colour "goose" strokeColour:(colour Red))
(piece Colour "wolf" strokeColour:(colour Red))
(piece ExtendName P2 piece:"lion" "1")
})
)
Any ideas what I might be missing?
Edit: swapped link for in-line code block