Commit 552fe5ff authored by Otto Wallenius's avatar Otto Wallenius Committed by Christian Persch
parent 7d7cde1d
......@@ -88,6 +88,7 @@ games_GUILE = \
triple-peaks.scm \
union-square.scm \
valentine.scm \
wall.scm \
westhaven.scm \
whitehead.scm \
will-o-the-wisp.scm \
......
;; AisleRiot - wall.scm
;; Copyright (C) 2015 Otto Wallenius <owalleni@gmail.com>, Markus Tuhkanen
;;
;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;;
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
(use-modules (aisleriot interface) (aisleriot api))
(define throne 0)
(define high-guard-post '(1 2 3))
(define low-guard-post '(4 5 6 7))
(define first-low-guard-slot-id 4)
(define first-wall-slot-id 8)
(define last-wall-slot-id 52)
(define wall '(8 9 10 11 12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31 32 33
34 35 36 37 38 39 40
41 42 43 44 45 46 47 48
49 50 51 52))
(define first-target-slot-id 0)
(define stock 53)
(define waste '(54 55 56))
(define (new-game)
(initialize-playing-area)
(set-ace-low)
(make-standard-double-deck)
; remove kings of spades from DECK
(set! DECK (filter
(lambda (card) (not (and
(= (get-value card) king)
(= (get-suit card) spade))))
DECK))
(shuffle-deck)
; throne
(set! HORIZPOS (+ HORIZPOS 0.5))
(add-blank-slot)
(add-blank-slot)
(add-blank-slot)
(add-partially-extended-slot '() right 2)
; high-guard-post
(add-carriage-return-slot)
(add-blank-slot)
(set! HORIZPOS (+ HORIZPOS 0.5))
(add-partially-extended-slot '() right 2)
(add-blank-slot)
(add-partially-extended-slot '() right 2)
(add-blank-slot)
(add-partially-extended-slot '() right 2)
; low-guard-post
(add-carriage-return-slot)
(set! HORIZPOS (+ HORIZPOS 0.5))
(add-partially-extended-slot '() right 3)
(add-blank-slot)
(add-partially-extended-slot '() right 3)
(add-blank-slot)
(add-partially-extended-slot '() right 3)
(add-blank-slot)
(add-partially-extended-slot '() right 3)
(add-blank-slot)
; wall
(add-carriage-return-slot)
(add-empty-normal-slots 8)
(add-carriage-return-slot)
(set! VERTPOS (- VERTPOS 0.5))
(set! HORIZPOS (+ HORIZPOS 0.5))
(add-empty-normal-slots 7)
(add-carriage-return-slot)
(set! VERTPOS (- VERTPOS 0.5))
(add-empty-normal-slots 8)
(add-carriage-return-slot)
(set! VERTPOS (- VERTPOS 0.5))
(set! HORIZPOS (+ HORIZPOS 0.5))
(add-empty-normal-slots 7)
(add-carriage-return-slot)
(set! VERTPOS (- VERTPOS 0.5))
(add-empty-normal-slots 8)
(add-carriage-return-slot)
(set! VERTPOS (- VERTPOS 0.5))
(set! HORIZPOS (+ HORIZPOS 0.5))
(add-empty-normal-slots 7)
; deck
(add-carriage-return-slot)
(set! VERTPOS (+ VERTPOS 0.5))
(add-blank-slot)
(add-blank-slot)
(add-normal-slot DECK)
; waste
(add-partially-extended-slot '() right 3)
(add-partially-extended-slot '() right 3)
(add-partially-extended-slot '() right 3)
; deal cards
(deal-cards-face-up stock (append high-guard-post high-guard-post))
(deal-cards stock (append low-guard-post low-guard-post low-guard-post))
(deal-cards-face-up stock wall)
(do-deal-next-cards)
(add-card! throne (make-visible (make-card king spade)))
(add-card! throne (make-visible (make-card king spade)))
(give-status-message)
(list 8 8)
)
(define (add-empty-normal-slots n)
(if (> n 0)
(begin (add-normal-slot '())
(add-empty-normal-slots (- n 1)))))
(define (give-status-message)
(set-statusbar-message (get-attacks-left-string)))
(define (get-attacks-left-string)
(string-append (_"Deals left: ")
(number->string (/ (length (get-cards stock)) 3))))
(define (in-wall? slot-id)
(and (>= slot-id first-wall-slot-id)
(<= slot-id last-wall-slot-id)))
(define (in-left-wall-edge? slot-id)
(and (in-wall? slot-id)
(= (modulo (- slot-id first-wall-slot-id) 15) 0)))
(define (in-right-wall-edge? slot-id)
(and (in-wall? slot-id)
(= (modulo (- slot-id first-wall-slot-id) 15) 7)))
(define (in-low-guard-post? slot-id)
(memv slot-id low-guard-post))
(define (in-high-guard-post? slot-id)
(memv slot-id high-guard-post))
(define (in-waste? slot-id)
(memv slot-id waste))
(define (in-throne? slot-id)
(= slot-id throne))
(define (is-target-slot? slot-id)
(or (in-throne? slot-id)
(in-high-guard-post? slot-id)
(in-low-guard-post? slot-id)
(in-wall? slot-id)))
; If the player attacks a "no-retreat-slot", the attack
; cards are discarded even if the attack fails.
(define (no-retreat? slot-id)
(and (exposed? slot-id)
(in-low-guard-post? slot-id)
(not (empty-slot? slot-id))
(not (is-visible? (get-top-card slot-id)))))
; Returns #t iff there's a low guard post pile that is both hidden and exposed.
(define (hidden-exposed-low-guard?)
(or-map (lambda (slot-id)
(and
(exposed? slot-id)
(not (empty-slot? slot-id))
(not (is-visible? (get-top-card slot-id)))))
low-guard-post))
(define (droppable? start-slot-id card-list end-slot-id)
(and (not (= start-slot-id end-slot-id))
(in-waste? start-slot-id)
(or
(in-waste? end-slot-id)
(and
(is-target-slot? end-slot-id)
(or
(attackers-would-win? card-list end-slot-id)
(and (exposed? end-slot-id)
(no-retreat? end-slot-id)))))))
; Makes visible cards that by the rules should be made
; visible after removing cards in slot slot-id.
(define (make-visible-if-possible slot-id)
(if (in-low-guard-post? slot-id)
(if (and
(not (empty-slot? slot-id))
(not (is-visible? (get-top-card slot-id))))
(make-cards-visible slot-id))
#f))
; Makes all cards visible in slot slot-id.
(define (make-cards-visible slot-id)
(set-cards! slot-id (map make-visible (get-cards slot-id))))
; Returns #t if attack succeeded, #f otherwise. If attack succeeds
; the cards in target-slot-id are removed.
(define (attack attackers target-slot-id source-slot-id)
(if (attackers-would-win? attackers target-slot-id)
(let* ([remove-fixed
(lambda () (remove-n-cards target-slot-id (length (get-cards target-slot-id))))])
(add-to-score! (get-hp target-slot-id))
; if pile face down, show the cards quickly before discarding them
(if (and
(in-low-guard-post? target-slot-id)
(not (is-visible? (get-top-card target-slot-id))))
(begin
(make-visible-if-possible target-slot-id)
(delayed-call remove-fixed))
(remove-fixed))
#t)
(begin
(if (no-retreat? target-slot-id)
(make-visible-if-possible target-slot-id))
#f)))
; Returns the lowest sum of card values minus one that can remove the cards in slot slot-id.
(define (get-hp slot-id)
(if (empty-slot? slot-id)
#f
(let* ([lpid (get-left-parent-slot-id slot-id)]
[rpid (get-right-parent-slot-id slot-id)]
[lrpid (if lpid (get-right-parent-slot-id lpid) #f)]
[rlpid (if rpid (get-left-parent-slot-id rpid) #f)])
(cond
[(in-left-wall-edge? slot-id)
(card-value-sum
(append (get-cards slot-id)
(if rpid (get-cards rpid) '())
(if rlpid (get-cards rlpid) '())))]
[(in-right-wall-edge? slot-id)
(card-value-sum
(append (get-cards slot-id)
(if lpid (get-cards lpid) '())
(if lrpid (get-cards lrpid) '())))]
[(in-wall? slot-id)
(card-value-sum
(append (get-cards slot-id)
(if lpid (get-cards lpid) '())
(if rpid (get-cards rpid) '())))]
[(or (is-target-slot? slot-id))
(card-value-sum (get-cards slot-id))]
[else #f]))))
; Returns #t if attackers would win if they attacked target-slot-id, #f otherwise.
(define (attackers-would-win? attackers target-slot-id)
(and (exposed? target-slot-id)
(not (null? attackers))
(not (empty-slot? target-slot-id))
(< (get-hp target-slot-id) (card-value-sum attackers))))
(define (card-value-sum card-list)
(if (null? card-list)
0
(+ (get-value (car card-list)) (card-value-sum (cdr card-list)))))
(define (get-left-child-slot-id slot-id)
(if (or (< slot-id first-wall-slot-id)
(> slot-id (- last-wall-slot-id 7))
(= (modulo (- slot-id first-wall-slot-id) 15) 0))
#f
(+ slot-id 7)))
(define (get-right-child-slot-id slot-id)
(if (or (< slot-id first-wall-slot-id)
(> slot-id (- last-wall-slot-id 7))
(= (modulo (- slot-id first-wall-slot-id) 15) 7))
#f
(+ slot-id 8)))
(define (get-left-parent-slot-id slot-id)
(if (or (< slot-id (+ first-wall-slot-id 8))
(> slot-id last-wall-slot-id)
(= (modulo (- slot-id first-wall-slot-id) 15) 0))
#f
(- slot-id 8)))
(define (get-right-parent-slot-id slot-id)
(if (or (< slot-id (+ first-wall-slot-id 8))
(> slot-id last-wall-slot-id)
(= (modulo (- slot-id first-wall-slot-id) 15) 7))
#f
(- slot-id 7)))
(define (get-left-sibling-slot-id slot-id)
(if (or (not (in-wall? slot-id))
(memv (modulo (- slot-id first-wall-slot-id) 15) '(0 8)))
#f
(- slot-id 1)))
(define (get-right-sibling-slot-id slot-id)
(if (or (not (in-wall? slot-id))
(memv (modulo (- slot-id first-wall-slot-id) 15) '(7 14)))
#f
(+ slot-id 1)))
; If a card is exposed, it means that one can attack it.
(define (exposed? slot-id)
(cond [(in-wall? slot-id)
(let ([lcid (get-left-child-slot-id slot-id)]
[rcid (get-right-child-slot-id slot-id)]
[lsid (get-left-sibling-slot-id slot-id)]
[rsid (get-right-sibling-slot-id slot-id)])
(or (and (not lcid) (not rcid))
(and lcid (empty-slot? lcid))
(and rcid (empty-slot? rcid))
(and lsid (empty-slot? lsid))
(and rsid (empty-slot? rsid))))]
[(in-throne? slot-id) (and-map empty-slot? high-guard-post)]
[(in-low-guard-post? slot-id)
(or
(empty-slot? (+ (* (- slot-id first-low-guard-slot-id) 2) first-wall-slot-id))
(empty-slot? (+ (* (- slot-id first-low-guard-slot-id) 2) first-wall-slot-id 1))
(and
(memv slot-id (cdr low-guard-post))
(empty-slot? (- slot-id 1)))
(and (memv slot-id (list-head low-guard-post 3))
(empty-slot? (+ slot-id 1))))]
[(in-high-guard-post? slot-id)
(or (empty-slot? (+ slot-id 3))
(empty-slot? (+ slot-id 4))
(empty-slot? (cadr high-guard-post))
(and (= slot-id (cadr high-guard-post))
(or (empty-slot? (car high-guard-post))
(empty-slot? (caddr high-guard-post)))))]
[else #f]))
; Returns the slot id of the first face-up slot that you can attack and win
; with the cards in waste, or #f if there is no such slot.
(define (find-visible-winnable-slot)
(find-visible-winnable-slot-rec first-target-slot-id))
(define (find-visible-winnable-slot-rec slot-id)
(if (not (is-target-slot? slot-id))
#f
(if (and
(attackers-would-win? (get-cards-from-slots waste) slot-id)
(is-visible? (get-top-card slot-id)))
slot-id
(find-visible-winnable-slot-rec (+ slot-id 1)))))
(define (do-deal-next-cards)
(if (not (empty-slot? stock))
(begin
(remove-n-cards (car waste) (length (get-cards (car waste))))
(remove-n-cards (cadr waste) (length (get-cards (cadr waste))))
(remove-n-cards (caddr waste) (length (get-cards (caddr waste))))
(deal-cards-face-up stock waste)
#t)
#f))
(define (button-clicked slot-id)
(cond
[(= slot-id stock)
(do-deal-next-cards)]
[(is-target-slot? slot-id)
(let ([was-no-retreat? (no-retreat? slot-id)])
(if (or
(attack (get-cards-from-slots waste) slot-id waste)
was-no-retreat?)
(begin
(empty-slots! waste)
(do-deal-next-cards)
#t)
#f))]
[else #f]))
(define (button-double-clicked slot-id)
(if (is-target-slot? slot-id)
(begin
(attack (get-cards-from-slots waste) slot-id waste)
(empty-slots! waste)
(do-deal-next-cards)
#t)
#f))
(define (button-pressed slot-id card-list)
(in-waste? slot-id))
(define (button-released start-slot card-list end-slot)
(cond [(is-target-slot? end-slot)
; no-retreat? must be evaluated before attack for correct result.
(let ([was-no-retreat? (no-retreat? end-slot)])
(let* ([attack-succeeded? (attack card-list end-slot #f)])
(if (and-map empty-slot? waste)
(do-deal-next-cards))
(or attack-succeeded?
was-no-retreat?)))]
[(memv end-slot waste)
(move-n-cards! start-slot end-slot card-list)]
[else #f]))
(define (empty-slots! slot-list)
(if (not (null? slot-list))
(begin
(remove-n-cards (car slot-list) (length (get-cards (car slot-list))))
(empty-slots! (cdr slot-list)))))
(define (get-cards-from-slots slot-list)
(if (null? slot-list)
'()
(append
(get-cards (car slot-list))
(get-cards-from-slots (cdr slot-list)))))
(define (game-continuable)
(give-status-message)
(and (not (game-won))
(or (not (empty-slot? stock))
(find-visible-winnable-slot)
(and (not (and-map empty-slot? waste))
(hidden-exposed-low-guard?)))))
(define (game-won)
(empty-slot? throne))
(define (dealable?)
(if (not (empty-slot? stock))
(list 0 (_"Deal cards."))
#f))
(define (get-hint)
(let ([winnable-slot (find-visible-winnable-slot)])
; if there's a hidden, exposed low guard post pile
; and attacking it is the best choice you have
(if (and (hidden-exposed-low-guard?)
(or (not winnable-slot)
(in-wall? winnable-slot)))
(list 0 (_"Attack a face-down pile."))
(if winnable-slot
(hint-remove-top-card winnable-slot)
(dealable?)))))
(define (get-options) #f)
(define (apply-options options) #f)
(define (timeout) #f)
(set-features droppable-feature dealable-feature)
(set-lambda new-game button-pressed button-released button-clicked
button-double-clicked game-continuable game-won get-hint get-options
apply-options timeout droppable? dealable?)
......@@ -289,6 +289,7 @@ been coded for your pleasure in the GNOME scripting language (Scheme).
<xi:include href="triple_peaks.xml" />
<xi:include href="union_square.xml" />
<xi:include href="valentine.xml" />
<xi:include href="wall.xml" />
<xi:include href="westhaven.xml" />
<xi:include href="whitehead.xml" />
<xi:include href="will_o_the_wisp.xml" />
......
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
"http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd" [
]>
<sect1 id="Wall"><!--<sect1info>
<copyright>
<year>2015</year>
<holder>Otto Wallenius, Markus Tuhkanen</holder>
</copyright>
<author>
<firstname>Otto</firstname>
<surname>Wallenius</surname>
</author>
<address><email>owalleni@gmail.com</email></address>
</sect1info>-->
<title>Wall</title>
<sect2><title>Setup</title>
<informaltable>
<tgroup cols="2">
<tbody>
<row>
<entry>Type of Deck</entry>
<entry>Standard Double Deck</entry>
</row>
<row>
<entry>Stock</entry>
<entry>
Bottom left pile of 39 cards. Cards are turned over three at a time to Waste.
</entry>
</row>
<row>
<entry>Waste</entry>
<entry>
Three rightmost piles at the bottom row. To be taken from Stock one on to each at a time.
</entry>
</row>
<row>
<entry>Tableau</entry>
<entry>
Wall, Low Guard Post, High Guard Post and Throne.
</entry>
</row>
<row>
<entry>Wall</entry>
<entry>
45 cards in six rows.
</entry>
</row>
<row>
<entry>Low Guard Post</entry>
<entry>
Four piles of three cards, placed face down initially.
</entry>
</row>
<row>
<entry>High Guard Post</entry>
<entry>
Three piles of two cards.
</entry>
</row>
<row>
<entry>Throne</entry>
<entry>
A pile with two kings of spades.
</entry>
</row>
</tbody>
</tgroup>
</informaltable>
</sect2>
<sect2><title>Goal</title>
<para>
Remove the two kings in Throne.
</para>
</sect2>
<sect2><title>Rules</title>
<para>
You start from the bottom of Wall and try to move up to Throne by removing cards on the way. You can remove cards by attacking them with Waste cards that are dealt three at a time from Stock. If Stock runs out and you cannot remove the kings in Throne with the remaining cards in Waste, the game is lost.
</para>
<para>
Waste cards used in attacks are called <emphasis>attack cards</emphasis>. An attack on any card or pile succeeds if the <emphasis>health</emphasis> of the attack cards exceeds the health of the attacked card or pile. Health is computed as a sum of card values as explained below. A successful attack removes both the attacked cards and the attack cards.
</para>
<sect3><title>Order of removing cards</title>
<para>The basic rule is to remove cards immediately above and next to already empty Tableau slots.</para>
<variablelist>
<varlistentry><term>Wall</term>
<listitem>
<para>From the beginning all cards in the bottom row of Wall can be removed. Furthermore, you can remove any card if a card covering its bottom left or right corner has been removed or a card in the same (horizontal) row next to it has been removed.</para>
</listitem>
</varlistentry>
<varlistentry><term>Low Guard Post</term>
<listitem>
<para>A pile can be removed when one of the two cards in Wall immediately below it has been removed or a pile next to it in Low Guard Post has been removed.</para>
</listitem>
</varlistentry>
<varlistentry><term>High Guard Post</term>
<listitem>
<para>A pile can be removed once the pile closest to its bottom left corner or the pile closest to its bottom right corner in Low Guard Post has been removed, or a pile next to it in High Guard Post has been removed.</para>
</listitem>
</varlistentry>
<varlistentry><term>Throne</term>
<listitem>
<para>Can be removed when all piles in High Guard Post have been removed.</para>
</listitem>
</varlistentry>
</variablelist>
</sect3>
<sect3><title>Health</title>
<para>
For attack cards, health is the sum of card values.
</para>
<para>
For most Wall cards, health is the sum of values of the card and the cards touching its top left and right corners. For the cards in the top row of Wall, health is just the card's value. For the cards on the left and right edge of Wall, health is the sum of values of the card, the only card touching its top corner and the card immediately above it.
</para>
<para>
For the piles in Low Guard Post, High Guard Post and Throne, a whole pile must be removed at once. A pile's health is the sum of values of the cards in that pile.
</para>
<para>
Ace is low.
</para>
</sect3>
<sect3><title>Attacking</title>
<para>
You can attack with any combination of Waste cards by piling them and dragging the pile on the card you want to attack. Clicking a Tableau card attacks the clicked card with all cards in Waste. New cards can be dealt from Stock on to Waste at any time. Dealing new cards discards the cards that were in Waste before.
</para>
<para>
The piles in Low Guard Post behave differently from others. The first attack on any of these piles flips the cards in the pile face up and removes the attack cards. If this first attack succeeds, the pile is removed as normal. If the attack fails, subsequent failing attacks on that pile do not cause attack cards to be removed.
</para>
</sect3>
<sect3><title>Other rules</title>
<para>
Double-clicking a Tableau card is an action shortcut. It has the same effect as clicking (see Attacking) and in addition causes new cards to be dealt from Stock on to Waste regardless of whether the attack was successful or not.
</para>
</sect3>
</sect2>
<sect2><title>Scoring</title>
<para>
Each successful attack scores the health of the attacked card or pile.
</para>
</sect2>
<sect2><title>Strategy</title>
<para>
It is best to move as directly up as possible. Try to find an easy path through Wall before starting off.
</para>
<para>
Try not to waste cards, but make as many attacks as possible before dealing new cards.
</para>
<para>
When trying to remove a Low Guard Post pile, it is often a good idea to first flip the cards over by attacking them with a low-valued card, and after that to decide whether to try through there or through some other Low Guard Post pile. Note that the expectation of a Low Guard Post pile's health is a little less than 21.
</para>
</sect2>
</sect1>
......@@ -93,6 +93,7 @@ HELP_FILES = \
triple_peaks.xml \
union_square.xml \
valentine.xml \
wall.xml \
westhaven.xml \
whitehead.xml \
will_o_the_wisp.xml \
......
......@@ -57,7 +57,7 @@ Cover, Elevator, Fortress, Giant, Spider, Gaps, Bakers Dozen, Whitehead,
Freecell, Helsinki, Spider Three Decks, Scuffle, Poker, Klondike Three Decks,
Valentine, Royal East, Thumb And Pouch, Klondike, Doublets, Template, Golf,
Westhaven, Beleaguered Castle, Hopscotch, Eliminator, Aunt Mary,
Hamilton
Hamilton, Wall
.RE
.SH OPTIONS
......
......@@ -104,6 +104,7 @@ games/treize.scm
games/triple-peaks.scm
games/union-square.scm
games/valentine.scm
games/wall.scm
games/westhaven.scm
games/whitehead.scm
games/will-o-the-wisp.scm
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment