summaryrefslogtreecommitdiff
path: root/ipl/gprogs/concen.icn
diff options
context:
space:
mode:
Diffstat (limited to 'ipl/gprogs/concen.icn')
-rw-r--r--ipl/gprogs/concen.icn243
1 files changed, 243 insertions, 0 deletions
diff --git a/ipl/gprogs/concen.icn b/ipl/gprogs/concen.icn
new file mode 100644
index 0000000..b00f9f4
--- /dev/null
+++ b/ipl/gprogs/concen.icn
@@ -0,0 +1,243 @@
+############################################################################
+#
+# File: concen.icn
+#
+# Subject: Program to play solitaire card game Concentration
+#
+# Author: Gregg M. Townsend
+#
+# Date: December 4, 1997
+#
+############################################################################
+#
+# This file is in the public domain.
+#
+############################################################################
+#
+# usage: concen [winoptions] [ncards]
+#
+# Concentration, as presented here, is a simple solitaire game.
+# When the program starts, there are 52 playing cards, face down.
+# They may be turned over by clicking on them with the mouse. Only
+# two cards may be face up at a time; if they are the same rank
+# (e.g. two sevens), they are removed. The object is to clear the
+# table.
+#
+# (For an interesting discussion of two-person Concentration, see
+# Ian Stewart's "Mathematical Recreations" column in the October,
+# 1991, edition of Scientific American, entitled "Concentration:
+# A Winning Strategy".)
+#
+############################################################################
+#
+# Requires: Version 9 graphics
+#
+############################################################################
+#
+# Links: drawcard, options, optwindw, random, graphics
+#
+############################################################################
+
+link drawcard
+link options
+link optwindw
+link random
+link graphics
+
+global deck # full deck of cards
+global nleft # number of cards left
+global nup # number of cards face up
+global uprank # rank of upturned cards, if all same
+
+global ncols, nrows # number of columns and rows
+global cardw, cardh # card width and height
+global margin, gap # outside margin, gap between cards
+global mono # GC for pattern, iff mono screen
+
+global cd # card record, indexed by position
+record cdrec(
+ label, # member of &letters as per Icon book
+ status) # status flag
+global VACANT, DOWN, UP # status flag values
+
+# main program.
+
+procedure main(args)
+ local i, j, e
+
+ initialize(args)
+ newgame()
+ while e := Event() do {
+ if e === QuitEvents() then
+ break
+ if e === (&lrelease | &mrelease | &rrelease) then {
+ i := (&y - margin + gap/2) / (cardh + gap)
+ j := (&x - margin + gap/2) / (cardw + gap)
+ click(i, j)
+ }
+ }
+end
+
+# initialize(args) -- process options, initialize globals, open window
+
+procedure initialize(args)
+ local opts, ncards
+
+ cardw := 80
+ cardh := 124
+ VACANT := 0
+ DOWN := 1
+ UP := 2
+
+ opts := options(args, winoptions()) # get command options
+
+ ncards := integer(args[1]) | 52 # get size of deck
+ ncards -:= ncards % 2 # ensure even
+ ncards <:= 2 # ensure at least 2 cards
+ ncards >:= 52 # ensure at most 52 cards
+ deck :=
+ ("aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ" ? move(ncards))
+
+ if ncards <= 10 then
+ nrows := 2
+ else if ncards <= 21 then
+ nrows := 3
+ else if ncards <= 36 then
+ nrows := 4
+ else
+ nrows := 5
+ ncols := (ncards + nrows - 1) / nrows
+
+ /opts["M"] := 20
+ margin := opts["M"]
+ gap := margin / 2
+ /opts["W"] := ncols * cardw + (ncols - 1) * gap
+ /opts["H"] := nrows * cardh + (nrows - 1) * gap
+ /opts["B"] := "deep moderate green"
+ &window := optwindow(opts)
+ if WAttrib("depth") = 1 then {
+ mono := Clone(&window, "fg=white", "bg=black", "fillstyle=textured")
+ Pattern(mono, "4,2,8,2,8")
+ FillRectangle(mono, 0, 0, 2 * margin + opts["W"], 2 * margin + opts["H"])
+ }
+ randomize()
+ return
+end
+
+# newgame() -- lay out cards, face down, for a new game
+
+procedure newgame()
+ local i, j, s
+
+ nleft := *deck
+ nup := 0
+ cd := []
+ every put(cd, cdrec(!deck, DOWN))
+ every i := *cd to 2 by -1 do
+ cd[?i] :=: cd[i]
+
+ every i := 0 to nrows-1 do
+ every j := 0 to ncols-1 do
+ if cardno(i, j) then
+ setcard(i, j, "-")
+
+ return
+end
+
+# click(i, j) -- process a click on the card in row i, column j
+
+procedure click(i, j)
+ local c
+
+ case nup of { # action depends on the number of cards already face up
+
+ 0: {
+ # no cards are face up. turn this one up.
+ c := cd[cardno(i, j)] | fail
+ if c.status = DOWN then {
+ setcard(i, j, c.label)
+ c.status := UP
+ nup := 1
+ uprank := crank(c.label)
+ }
+ }
+
+ 1: {
+ # one is face up. it might be the one clicked.
+ c := cd[cardno(i, j)] | fail
+ if c.status = UP then {
+ setcard(i, j, "-")
+ c.status := DOWN
+ nup := 0
+ }
+ else if c.status = DOWN then {
+ setcard(i, j, c.label)
+ c.status := UP
+ nup := 2
+ if uprank ~= crank(c.label) then
+ uprank := &null
+ }
+ }
+
+ 2: {
+ # two are face up. it doesn't matter what card was clicked.
+ # remove the two up-cards if they match, or turn back over if not.
+ every i := 0 to nrows-1 do
+ every j := 0 to ncols-1 do
+ if c := cd[cardno(i, j)] then
+ if c.status = UP then {
+ if \uprank then {
+ setcard(i, j, &null)
+ c.status := VACANT
+ nleft -:= 1
+ }
+ else {
+ setcard(i, j, "-")
+ c.status := DOWN
+ }
+ nup -:= 1
+ }
+ # if no cards are left, the game is won.
+ # show all cards face up as a reward.
+ if nleft = 0 then
+ every i := 0 to nrows-1 do
+ every j := 0 to ncols-1 do
+ if c := cd[cardno(i, j)] then {
+ setcard(i, j, c.label)
+ c.status := UP
+ nup +:= 1
+ }
+ }
+ default:
+ # presumably there are 52 cards face up after a win.
+ # start a new game with this new click.
+ newgame()
+ }
+ return
+end
+
+# setcard(i, j, c) -- redraw card c at location (i,j), or background if /c
+
+procedure setcard(i, j, c)
+ local x, y
+ x := margin + j * (cardw + gap)
+ y := margin + i * (cardh + gap)
+ drawcard(x, y, \c) |
+ FillRectangle(\mono, x, y, cardw, cardh) |
+ EraseArea(x, y, cardw, cardh)
+ return
+end
+
+# cardno(i, j) -- return index (1 to 52) if location is valid
+
+procedure cardno(i, j)
+ return (0 <= i) & (0 <= j < ncols) & *deck >= (ncols * i + j + 1)
+end
+
+# crank(label) -- return rank (1 to 13) of card with given label
+
+procedure crank(label)
+ static fulldeck
+ initial fulldeck := string(&letters)
+ return fulldeck ? find(label) % 13
+end