############################################################################ # # File: yahtz.icn # # Subject: Program to play yahtzee # # Author: Chris Tenaglia # # Date: March 3, 1996 # ############################################################################ # # This file is in the public domain. # ############################################################################ # # Version: 1.3 # ############################################################################ # # Modified by Richard Goerwitz with corrections by Phillip Lee Thomas # ############################################################################ # # This hacked version will run under UNIX, and under DOS as well. It # should run out of the box on DOS as long as you stay in the current # directory. See the README file. # # This is a test version!! In accordance with the author's wishes, # I'd like to make it clear that I've altered all the screen I/O # routines, and have removed characters peculiar to VT terminals. # I've tried to keep intact the author's indentation and brace style. # Changes, where present, have been indicated by my initials. The # IPL-style header was added by me. # # -Richard Goerwitz. # ############################################################################ # # Links: iolib, random # ############################################################################ link iolib link random global players,slot,team,d,od,dice,round procedure main(param) paint() assign_players() every round := 1 to 13 do every play(!team) summarize() end # # DISPLAYS THE HEADER AND SEPARATOR LINE AT BEGINNING OF GAME # procedure paint() # Clear first, separately. Screws up on some terminals of you don't. writes(cls()) # Check to be sure the terminal is big enough, and won't leave magic # cookies on the screen. -RLG if getval("ug"|"sg") > 0 then stop("abort: Can't do magic cookie terminals!") if getval("li") < 24 | getval("co") < 80 then stop("abort: Your terminal is too small!") write(high(uhalf(" Y A H T Z E E "))) write(high(lhalf(" Y A H T Z E E "))) write(at(1,10),graf(repl("=",75))) end # # DISPLAYS THE FINAL SCORE OF ALL THE PLAYERS # procedure summarize() local player, card, top, bottom, i # blink, high, inverse was just too much for my terminal to handle -RLG write(at(1,11), high(chop("Final Score Summary"))) every player := key(players) do { card := players[player] top := 0 ; every i := 1 to 6 do top +:= card[i] if top > 62 then top +:= 35 bottom := 0 ; every i := 7 to 13 do bottom +:= card[i] write("Player ",high(left(player,14))," Top = ",right(top,5), " Bottom = ",right(bottom,5), " Total = ",right(top+bottom,5)) } input("") end # # SETUP AND INITIALIZATION OF YAHTZEE ENVIRONMENT # procedure assign_players() local n, player n := 1 ; team := [] ; slot := [] ; d := list(6,"") ; od := list(5,0) randomize() players := table("n/a") repeat { (player := input(("Name of player #" || n || ": "))) | stop("Game called off.") if player == "" then break n +:= 1 put(team,player) players[player] := list(13,"*") } if n = 1 then stop("Nobody wants to play!") put(slot,"Ones") ; put(slot,"Twos") ; put(slot,"Threes") put(slot,"Fours") ; put(slot,"Fives") ; put(slot,"Sixes") put(slot,"3oK") ; put(slot,"4oK") ; put(slot,"FullH") put(slot,"SmStr") ; put(slot,"LgStr") ; put(slot,"Yahtzee") put(slot,"Chance") # VT-specific characters removed. -RLG d[1] := "+-----+| || o || |+-----+" d[2] := "+-----+| || o o || |+-----+" d[3] := "+-----+|o || o || o|+-----+" d[4] := "+-----+|o o|| ||o o|+-----+" d[5] := "+-----+|o o|| o ||o o|+-----+" d[6] := "+-----+|o o o|| ||o o o|+-----+" end # # THIS ROUTINE LETS A PLAYER TAKE THEIR TURN # procedure play(name) local shake, select writes(at(1,11),"It's ",high(name),"'s turn",chop()) writes(at(1,getval("li")-1),high(name)) input(", please press to begin.") score(name) dice := [] ; every 1 to 5 do put(dice,?6) depict() shake := obtain("Shake which ones : ") (shake === []) | (every dice[!shake] := ?6) depict() shake := obtain("Shake which ones (last chance) : ") (shake === []) | (every dice[!shake] := ?6) depict() repeat { select := input(at(1,22) || clip("Tally to which category (1-13) : ")) numeric(select) | next (1 <= select <= 13) | next (players[name][select] == "*") | next break } tally(name,select) score(name) input(at(1,22) || clip("Press ")) end # # THIS ROUTINE DRAWS THE DICE # procedure depict() local i, j, x every i := 1 to 5 do { x := 1 writes(at(i*10+3,3),inverse(i)) writes(at(i*10+4,9),inverse(dice[i])) every j := 4 to 8 do { # debug writes(at(i*10,j),graf(d[dice[i]][x:x+7])) x +:= 7 } od[i] := dice[i] } end # # THIS ROUTINE LETS THE PLAYER DECIDE WHAT TO APPLY THE SHAKES TO # procedure tally(name,area) local sum, unit, flag, tmp, piece, hold case integer(area) of { 1 : { # ones sum := 0 ; every unit := !dice do if unit = 1 then sum +:= 1 players[name][1] := sum } 2 : { # twos sum := 0 ; every unit := !dice do if unit = 2 then sum +:= 2 players[name][2] := sum } 3 : { # threes sum := 0 ; every unit := !dice do if unit = 3 then sum +:= 3 players[name][3] := sum } 4 : { # fours sum := 0 ; every unit := !dice do if unit = 4 then sum +:= 4 players[name][4] := sum } 5 : { # fives sum := 0 ; every unit := !dice do if unit = 5 then sum +:= 5 players[name][5] := sum } 6 : { # sixes sum := 0 ; every unit := !dice do if unit = 6 then sum +:= 6 players[name][6] := sum } 7 : { # 3 of a kind sum := 0 ; flag := 0 tmp := table(0) ; every unit := !dice do tmp[unit] +:= 1 every piece := key(tmp) do if tmp[piece] >= 3 then flag := 1 if flag = 1 then every sum +:= !dice players[name][7] := sum } 8 : { # four of a kind sum := 0 ; flag := 0 tmp := table(0) ; every unit := !dice do tmp[unit] +:= 1 every piece := key(tmp) do if tmp[piece] >= 4 then flag := 1 if flag = 1 then every sum +:= !dice players[name][8] := sum } 9 : { # full house sum := 0 ; flag := 0 tmp := table(0) ; every unit := !dice do tmp[unit] +:= 1 every piece := key(tmp) do { if tmp[piece] = 3 then flag +:= 1 if tmp[piece] = 2 then flag +:= 1 } if flag = 2 then sum := 25 players[name][9] := sum } 10 : { # small straight sum := 0 ; flag := 0 hold := set() ; every insert(hold,!dice) tmp := sort(hold) if tmp[1]+1 = tmp[2] & tmp[2]+1 = tmp[3] & tmp[3]+1 = tmp[4] then flag := 1 if tmp[2]+1 = tmp[3] & tmp[3]+1 = tmp[4] & tmp[4]+1 = tmp[5] then flag := 1 if flag = 1 then sum := 30 players[name][10] := sum } 11 : { # large straight sum := 0 ; flag := 0 tmp := sort(dice) if tmp[1]+1 = tmp[2] & tmp[2]+1 = tmp[3] & tmp[3]+1 = tmp[4] & tmp[4]+1 = tmp[5] then flag := 1 if flag = 1 then sum := 40 players[name][11] := sum } 12 : { # yahtzee sum := 0 ; flag := 0 tmp := table(0) ; every unit := !dice do tmp[unit] +:= 1 every piece := key(tmp) do if tmp[piece] = 5 then flag := 1 if flag = 1 then sum := 50 players[name][12] := sum } 13 : { # chance sum := 0 ; every sum +:= !dice players[name][13] := sum } } end # # THIS ROUTINE OBTAINS A VALID SHAKER REQUEST # procedure obtain(prompt) local line, unit, units repeat { writes(at(1,22),prompt) (line := read()) | next if match("q",map(line)) then stop("Game Quit") if trim(line) == "" then return [] units := parse(line,', \t') every unit := !units do (1 <= unit <= 5) | next break } return units end # # THIS ROUTINE PAINTS THE SCORECARD FOR A GIVEN PLAYER # procedure score(name) local st1, st2, i, bonus # Slight realignment. -RLG writes(at(1,11),chop(),at(18,11),under(),"Player = ",name," Round = ",under(round)) writes(at(10,12)," 1 : Ones = ",players[name][1]) writes(at(10,13)," 2 : Twos = ",players[name][2]) writes(at(10,14)," 3 : Threes = ",players[name][3]) writes(at(10,15)," 4 : Fours = ",players[name][4]) writes(at(10,16)," 5 : Fives = ",players[name][5]) writes(at(10,17)," 6 : Sixes = ",players[name][6]) writes(at(40,12)," 7 : 3oK = ",players[name][7]) writes(at(40,13)," 8 : 4oK = ",players[name][8]) writes(at(40,14)," 9 : FullH = ",players[name][9]) writes(at(40,15),"10 : SmStr = ",players[name][10]) writes(at(40,16),"11 : LgStr = ",players[name][11]) writes(at(40,17),"12 : Yahtzee = ",players[name][12]) writes(at(40,18),"13 : Chance = ",players[name][13]) st1 := 0 ; every i := 1 to 6 do st1 +:= numeric(players[name][i]) if st1 > 62 then bonus := 35 else bonus := 0 st2 := 0 ; every i := 7 to 13 do st2 +:= numeric(players[name][i]) writes(at(10,19),"Bonus = ",clip(bonus)) writes(at(10,20),"Subtotal = ",st1+bonus) writes(at(40,20),"Subtotal = ",st2) writes(at(37,21),"Grand Total = ",st1+st2+bonus) end # # From here down, all CT's VT-specific I/O codes have been replaced # with calls to iolib/itlib routines. The replacements were quite # easy to do because of the great modularity of the original program. # -RLG # # # VIDEO ROUTINE CLEARS SCREEN # procedure cls(str) static clear_string initial { clear_string := getval("cl") | (igoto(getval("cm"),1,1) || getval("cd")) | stop("abort: Your terminal can't clear screen!") } /str := "" return clear_string || str end # # VIDEO ROUTINE ERASES REST OF SCREEN # procedure chop(str) static clear_rest initial { clear_rest := getval("cd") | stop("abort: Sorry, your terminal must have cd capability.") } /str := "" return clear_rest || str end # # VIDEO ROUTINE OUTPUTS UPPER HALF OF DOUBLE SIZE MESSAGES # procedure uhalf(str) # Disabled for non-VT{2,3,4}XX terminals. I'd have left them in for # vt100s, but there are so many vt100 terminal emulation programs out # there that don't do the big characters that I thought better of it. # -RLG static isVT initial { if map(getname()) ? (tab(find("vt")+2), tab(any('234')), integer(tab(0))) then isVT := 1 } if \isVT then { /str := "" if str == "" then return "\e#3" return "\e#3" || str } end # # VIDEO ROUTINE OUTPUTS BOTTOM HALF OF DOUBLE SIZE MESSAGES # procedure lhalf(str) static isVT initial { if map(getname()) ? (tab(find("vt")+2), tab(any('234')), integer(tab(0))) then isVT := 1 } if \isVT then { /str := "" if str == "" then return "\e#4" return "\e#4" || str } end # # VIDEO ROUTINE OUTPUTS STRING AND CLEARS TO EOL # procedure clip(str) static clear_line initial { clear_line := getval("ce") | " " } /str := "" if str == "" then return clear_line return str ||:= clear_line end # # VIDEO ROUTINE OUTPUTS HIGHLIGHTED STRINGS # procedure high(str) static bold_code, off_other_modes initial { off_other_modes := "" every off_other_modes ||:= getval("me"|"ue"|"se") bold_code := off_other_modes || getval("md"|"us"|"so") } /str := "" return bold_code || str || off_other_modes end # # VIDEO ROUTINE OUTPUTS INVERSE VIDEO STRINGS # procedure inverse(str) static reverse_code, off_other_modes initial { off_other_modes := "" every off_other_modes ||:= getval("se"|"ue"|"me") reverse_code := off_other_modes || getval("so"|"us"|"md") } /str := "" return reverse_code || str || off_other_modes end # # VIDEO ROUTINE OUTPUTS UNDERLINED STRINGS # procedure under(str) static underline_code, off_other_modes initial { off_other_modes := "" every off_other_modes ||:= getval("ue"|"me"|"se") underline_code := off_other_modes || getval("us"|"md"|"so") } /str := "" return underline_code || str || off_other_modes end # # VIDEO ROUTINE OUTPUTS BLINKING STRINGS # procedure blink(str) static blink_code, off_other_modes initial { off_other_modes := "" every off_other_modes ||:= getval("me"|"se"|"ue") blink_code := off_other_modes || getval("mb"|"md"|"so"|"us") } /str := "" return blink_code || str || off_other_modes end # # VIDEO ROUTINE SETS NORMAL VIDEO MODE # procedure norm(str) static off_modes initial { off_modes := "" every off_modes ||:= getval("me"|"se"|"ue") } /str := "" return off_modes || str end # # VIDEO ROUTINE TURNS ON VT GRAPHICS CHARACTERS # procedure graf(str) # Again, disabled for non-VT{234}XX terminals. -RLG static isVT initial { if map(getname()) ? (tab(find("vt")+2), tab(any('234')), integer(tab(0))) then isVT := 1 } /str := "" if \isVT then { if str == "" then return "\e(0" str := "\e(0" || str if (str[-3:0] == "\e(B") then return str else return str || "\e(B" } else return str end # # VIDEO ROUTINE TURNS OFF VT GRAPHICS CHARACTERS # procedure nograf(str) static isVT initial { if map(getname()) ? (tab(find("vt")+2), tab(any('234')), integer(tab(0))) then isVT := 1 } /str := "" if \isVT then { if str == "" then return "\e(B" str := "\e(B" || str } return str end # # VIDEO ROUTINE SETS CURSOR TO GIVEN X,Y COORDINATES # procedure at(x,y) return igoto(getval("cm"), x, y) end ######### Here end the I/O routines I needed to alter. -RLG # # PARSES A STRING INTO A LIST WITH RESPECT TO A GIVEN DELIMITER # procedure parse(line,delims) local i, tokens static chars chars := &cset -- delims tokens := [] line ? while tab(upto(chars)) do put(tokens,tab(many(chars))) # # My first time playing, I didn't put spaces between the numbers # for the dice. When you think about it, though, why bother? # They can't be any longer than one digit each, so there's no # ambiguity. This bit of code makes the game a bit more idiot- # proof. -RLG (one of the idiots) # if *!tokens > 1 then line ? { tokens := [] if tab(upto(&digits)) then { while put(tokens, move(1)) do tab(upto(&digits)) | break put(tokens, integer(tab(0))) } } return tokens end # # TAKE AN INPUT STRING VIA GIVEN PROMPT # procedure input(prompt) writes(prompt) return read() end