2024-01-15 17:55:19 +02:00
|
|
|
# no-nonsense FreeCell in POSIX AWK
|
2024-01-17 22:40:16 +02:00
|
|
|
# run as: [n]awk -f nnfc.awk [-v ASCII=1] [-v MONOCHROME=1] [-v SEED=x]
|
2024-01-17 16:22:32 +02:00
|
|
|
# commands: q - quit, u - undo, h - help, r - restart the run
|
2024-01-16 12:25:31 +02:00
|
|
|
# [number][number] - move from column/freecell to column/freecell/foundation:
|
2024-01-15 17:54:17 +02:00
|
|
|
# - 0 is foundation (can be omitted)
|
|
|
|
|
# - 1 to 8 are column numbers
|
2024-01-16 12:25:31 +02:00
|
|
|
# - a, b, c, d are freecell numbers
|
2024-01-17 16:22:32 +02:00
|
|
|
# bulk move/supermove operation between columns is performed automatically when possible
|
2024-01-15 17:54:17 +02:00
|
|
|
# SEED variable can be used to init games M$-style
|
2024-01-15 17:49:11 +02:00
|
|
|
# created by Luxferre in 2024, released into public domain
|
|
|
|
|
|
|
|
|
|
# Helper functions
|
|
|
|
|
|
|
|
|
|
function alen(a, i, k) { # determine array length
|
|
|
|
|
k = 0
|
|
|
|
|
for(i in a) k++
|
|
|
|
|
return k
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# split a string with separator into an array but with 0-based indexes
|
|
|
|
|
function asplit(s, a, sep, len, i) {
|
|
|
|
|
split(s, a, sep) # get 1-based-index array in a
|
|
|
|
|
len = alen(a)
|
|
|
|
|
for(i=0;i<len;i++) a[i] = a[i+1] # move everything to the left
|
|
|
|
|
delete a[len] # delete the last element after moving
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# serialize any associative array into a string
|
|
|
|
|
# sep must be a single char not occurring in any key or value
|
|
|
|
|
function assocser(aa, sep, outs, k) {
|
|
|
|
|
outs = "" # initialize an ordered array string
|
|
|
|
|
for(k in aa) # iterate over keys
|
|
|
|
|
outs = outs sep k sep aa[k]
|
|
|
|
|
return substr(outs, 2) # delete the first sep
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# deserialize any associative array from a string
|
|
|
|
|
# sep must be a single char used when calling assocser() function
|
|
|
|
|
function assocdes(s, aa, sep, oa, i, len) {
|
|
|
|
|
split("", aa) # erase aa array
|
|
|
|
|
split(s, oa, sep) # get 1-based ordered array in oa
|
|
|
|
|
len = alen(oa) # ordered array length
|
|
|
|
|
for(i=1;i<=len;i+=2) # populate aa
|
|
|
|
|
aa[oa[i]] = oa[i+1]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# get card suit index (0=clubs, 1=diamonds, 2=hearts, 3=spades)
|
|
|
|
|
function getsuit(n) {return int(n/13)}
|
|
|
|
|
|
|
|
|
|
# get card color by numeric value: 0 is red, 1 is black
|
|
|
|
|
function getcolor(n, si) {si = getsuit(n); return (si > 0 && si < 3) ? 0 : 1}
|
|
|
|
|
|
2024-01-17 16:22:32 +02:00
|
|
|
# main card stacking condition
|
|
|
|
|
function stackcond(hi, lo) {
|
|
|
|
|
return (getcolor(hi) != getcolor(lo)) && ((lo%13) == (hi%13) - 1)
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-15 17:49:11 +02:00
|
|
|
# Traditional M$ FreeCell LCG for deal emulation
|
|
|
|
|
function fclcg() {
|
|
|
|
|
fc_lstate = (214013 * fc_lstate + 2531011) % 2147483648
|
|
|
|
|
return int(fc_lstate / 65536)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# render the playfield from cardstr, table, fcell and fnd arrays
|
|
|
|
|
function render(i, j, res) {
|
|
|
|
|
res = ""
|
|
|
|
|
# top left: free cells
|
|
|
|
|
for(i=0;i<4;i++) res = res sprintf("%s", cardstr[fcell[i]])
|
|
|
|
|
# top right: foundations
|
|
|
|
|
for(i=0;i<4;i++) res = res sprintf("%s", cardstr[fnd[i]])
|
|
|
|
|
res = res "\n"
|
|
|
|
|
# delimiter
|
|
|
|
|
for(i=0;i<23;i++) res = res "-"
|
|
|
|
|
res = res "\n"
|
|
|
|
|
# main table rendering
|
|
|
|
|
# max possible column length is 20 elements (8 + 12)
|
|
|
|
|
for(j=0;j<20;j++) {
|
|
|
|
|
for(i=0;i<8;i++) {
|
|
|
|
|
if((i,j) in table)
|
|
|
|
|
res = res cardstr[table[i,j]]
|
|
|
|
|
else res = res " "
|
|
|
|
|
}
|
|
|
|
|
res = res "\n"
|
|
|
|
|
}
|
|
|
|
|
sub(/[ \t\n\r]+$/, "", res) # trim all trailing whitespace
|
|
|
|
|
printf("\nGame #%u\n", fc_deal)
|
|
|
|
|
print res # finally print out the resulting field
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-17 16:22:32 +02:00
|
|
|
function undo(i) { # undo functionality
|
|
|
|
|
if(undoidx > 0) {
|
|
|
|
|
for(i=0;i<undomoves;i++) {
|
|
|
|
|
undoidx--
|
|
|
|
|
if(undoidx >= 0) {
|
|
|
|
|
assocdes(undos["table",undoidx], table, ",")
|
|
|
|
|
assocdes(undos["fnd",undoidx], fnd, ",")
|
|
|
|
|
assocdes(undos["fcell",undoidx], fcell, ",")
|
|
|
|
|
assocdes(undos["tablens",undoidx], tablens, ",")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if(undoidx < 0) undoidx = 0
|
|
|
|
|
} else print "Nowhere to undo!"
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-15 17:49:11 +02:00
|
|
|
# main logic/move function (also handles the undo)
|
2024-01-16 13:33:15 +02:00
|
|
|
# statuses: 0 - invalid move, 1 - continue, 2 - victory
|
2024-01-17 22:40:16 +02:00
|
|
|
function domove(cmd, isauto, valid, nums, from, to, amt, i, si, ec, efc) {
|
2024-01-15 17:49:11 +02:00
|
|
|
valid = 0 # invalid by default until all checks are done
|
2024-01-17 16:22:32 +02:00
|
|
|
# if cmd is not q, h, r or u, then it must be two "digits"
|
|
|
|
|
split(cmd, nums, "") # parse the command as a whole
|
2024-01-16 12:25:31 +02:00
|
|
|
from = nums[1] # location from which we're moving
|
|
|
|
|
to = nums[2] # location to which we're moving
|
2024-01-17 22:40:16 +02:00
|
|
|
if(to == "h") to = 0 # for compatibility with standard notation
|
2024-01-17 16:22:32 +02:00
|
|
|
amt = 1 # the amount of cards being moved (default 1)
|
2024-01-16 12:25:31 +02:00
|
|
|
# identify free cells
|
|
|
|
|
if(from == "a") from = 9
|
|
|
|
|
if(from == "b") from = 10
|
|
|
|
|
if(from == "c") from = 11
|
|
|
|
|
if(from == "d") from = 12
|
|
|
|
|
if(to == "a") to = 9
|
|
|
|
|
if(to == "b") to = 10
|
|
|
|
|
if(to == "c") to = 11
|
|
|
|
|
if(to == "d") to = 12
|
|
|
|
|
from = int(from)
|
|
|
|
|
to = int(to)
|
2024-01-15 17:49:11 +02:00
|
|
|
rfrom = from - 1 # real from location
|
|
|
|
|
rto = to - 1 # real to location
|
2024-01-17 16:22:32 +02:00
|
|
|
# if moving between columns, we can move over 1 card, detect how many
|
|
|
|
|
if(from > 0 && from < 9 && to > 0 && to < 9) { # both must be columns
|
|
|
|
|
# calculate how many cards we can theoretically move in a stack
|
|
|
|
|
# starting index is the last in the tableau
|
|
|
|
|
for(i=tablens[rfrom]-1;i>1;i--) { # check the primary condition
|
|
|
|
|
si = table[rfrom,i-1] # the previous value
|
|
|
|
|
cval = table[rfrom,i] # the current value
|
|
|
|
|
if(length(cval) == 0 || length(si) == 0) break # we're out of bounds
|
|
|
|
|
# increase the amount as long as the condition is satisfied
|
|
|
|
|
if(stackcond(si, cval)) amt++
|
2024-01-16 23:12:38 +02:00
|
|
|
}
|
2024-01-17 16:22:32 +02:00
|
|
|
# calculate the max amount of cards we're allowed to move
|
|
|
|
|
efc = 0 # count empty freecells here
|
|
|
|
|
ec = 0 # count empty columns and final result here
|
|
|
|
|
for(i=0;i<4;i++) if(fcell[i] == -1) efc++
|
|
|
|
|
for(i=0;i<8;i++) if(tablens[i] == 0) ec++
|
|
|
|
|
# now, calculate the max card amount to move
|
|
|
|
|
ec = (2^ec) * (efc + 1) # base formula
|
|
|
|
|
if(tablens[rto] == 0) ec /= 2 # halve if moving to an empty column
|
|
|
|
|
if(amt > ec) amt = ec # hard limit the amount of moved cards to the max
|
|
|
|
|
# now check how much of the stack actually matches the target
|
|
|
|
|
if(tablens[rto] > 0) { # we're moving to a non-empty column
|
|
|
|
|
efc = tablens[rfrom]-amt # index of the first stack element in the tableau
|
|
|
|
|
si = table[rto,tablens[rto]-1] # target value
|
|
|
|
|
for(i=efc;i<tablens[rfrom];i++) { # iterate over the stack
|
|
|
|
|
# subtract the amount as long as the condition is not met
|
|
|
|
|
if(stackcond(si, table[rfrom,i])) break
|
|
|
|
|
else amt--
|
|
|
|
|
}
|
|
|
|
|
if(amt < 1) amt = 1 # try moving one card in any case
|
|
|
|
|
}
|
2024-01-16 23:12:38 +02:00
|
|
|
}
|
2024-01-15 17:49:11 +02:00
|
|
|
if(from > 0 && from < 13 && to > -1 && to < 13) { # first check
|
|
|
|
|
# determine what card we're trying to move
|
|
|
|
|
cval = -1
|
|
|
|
|
if(from < 9) { moving from a non-empty tableau
|
|
|
|
|
if(tablens[rfrom] > 0 && length(table[rfrom,tablens[rfrom]-1]) > 0)
|
|
|
|
|
cval = int(table[rfrom,tablens[rfrom]-1])
|
|
|
|
|
} else cval = fcell[from - 9]
|
|
|
|
|
if(cval > -1) { # we are moving a non-empty value
|
|
|
|
|
if(to == 0) { # moving to a foundation
|
|
|
|
|
si = getsuit(cval) # get suit index
|
2024-01-17 22:40:16 +02:00
|
|
|
efc = cval % 13 # reuse for card rank value
|
|
|
|
|
if((fnd[si]%13) == (efc - 1)) { # we can move there
|
|
|
|
|
# check the automove constraints, aces and twos are always played
|
|
|
|
|
if(isauto && efc > 1) {
|
|
|
|
|
# check if all cards with efc-1 of the opposite color are there
|
|
|
|
|
if(getcolor(cval) == 0) { # we're red, check black
|
|
|
|
|
if(!(fnd[0] >= (efc - 1) && fnd[3] >= (efc - 1))) return 0
|
|
|
|
|
} else { # we're black, check red
|
|
|
|
|
if(!(fnd[1] >= (efc - 1) && fnd[2] >= (efc - 1))) return 0
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-01-15 17:49:11 +02:00
|
|
|
fnd[si] = cval
|
|
|
|
|
if(from < 9) { # moving from a tableau
|
|
|
|
|
delete table[rfrom,tablens[rfrom]-1] # delete the card
|
|
|
|
|
tablens[rfrom]-- # decrease tableau length
|
|
|
|
|
} else fcell[from - 9] = -1 # moving from a free cell
|
|
|
|
|
valid = 1 # mark the move as valid
|
|
|
|
|
}
|
|
|
|
|
} else if(to < 9) { # moving to a tableau
|
2024-01-16 23:12:38 +02:00
|
|
|
efc = 0 # reuse for starting index in source tableau
|
|
|
|
|
if(from < 9) efc = tablens[rfrom] - amt
|
|
|
|
|
if(efc > -1) { # check if we aren't moving more cards than allowed
|
|
|
|
|
for(i=0;i<amt;i++) { # move the cards as usual
|
|
|
|
|
if(from < 9) { # re-read the source value if looping over a column
|
|
|
|
|
cval = table[rfrom, efc+i]
|
|
|
|
|
if(cval == "") break # we try to read more than is available
|
|
|
|
|
else cval = int(cval)
|
|
|
|
|
}
|
|
|
|
|
si = table[rto,tablens[rto]-1] # target value
|
2024-01-17 16:22:32 +02:00
|
|
|
if(stackcond(si, cval) || length(si) == 0) {
|
2024-01-16 23:12:38 +02:00
|
|
|
table[rto,tablens[rto]] = cval # copy the card there
|
|
|
|
|
tablens[rto]++ # increase tableau length
|
|
|
|
|
if(from < 9) { # moving from a tableau
|
|
|
|
|
delete table[rfrom,efc+i] # delete the card
|
|
|
|
|
tablens[rfrom]-- # decrease tableau length
|
|
|
|
|
} else fcell[from - 9] = -1 # moving from a free cell
|
|
|
|
|
valid = 1 # mark the move as valid
|
|
|
|
|
} else break
|
|
|
|
|
}
|
2024-01-15 17:49:11 +02:00
|
|
|
}
|
|
|
|
|
} else if(fcell[to - 9] == -1) { # moving to a free cell
|
|
|
|
|
fcell[to - 9] = cval
|
|
|
|
|
if(from < 9) { # moving from a tableau
|
|
|
|
|
delete table[rfrom,tablens[rfrom]-1] # delete the card
|
|
|
|
|
tablens[rfrom]-- # decrease tableau length
|
2024-01-16 23:12:38 +02:00
|
|
|
} else fcell[from - 9] = -1 # moving from a free cell
|
2024-01-15 17:49:11 +02:00
|
|
|
valid = 1 # mark the move as valid
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if(valid == 1) { # the move is valid and performed
|
2024-01-17 16:22:32 +02:00
|
|
|
undomoves++ # increase recent move counter
|
2024-01-15 17:49:11 +02:00
|
|
|
undoidx++ # increment the undo index
|
|
|
|
|
# save the new playfield data under it
|
|
|
|
|
undos["table",undoidx] = assocser(table, ",")
|
|
|
|
|
undos["fnd",undoidx] = assocser(fnd, ",")
|
|
|
|
|
undos["fcell",undoidx] = assocser(fcell, ",")
|
|
|
|
|
undos["tablens",undoidx] = assocser(tablens, ",")
|
|
|
|
|
}
|
|
|
|
|
# now, check for the win condition: all kings in fnd
|
|
|
|
|
if(fnd[0] == 12 && fnd[1] == 25 && fnd[2] == 38 && fnd[3] == 51) return 2
|
2024-01-16 13:33:15 +02:00
|
|
|
else return valid # we can use the external move status
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# automatically move all eligible cards to the foundation
|
|
|
|
|
function autohome(chres, lres, i) {
|
|
|
|
|
do {
|
|
|
|
|
chres = 0
|
|
|
|
|
for(i=1;i<9;i++) {
|
2024-01-17 22:40:16 +02:00
|
|
|
lres = domove(i "0", 1)
|
2024-01-16 13:33:15 +02:00
|
|
|
if(lres == 2) return 2
|
|
|
|
|
else chres += lres
|
|
|
|
|
}
|
2024-01-17 22:40:16 +02:00
|
|
|
lres = domove("a0", 1)
|
2024-01-16 13:33:15 +02:00
|
|
|
if(lres == 2) return 2
|
|
|
|
|
else chres += lres
|
2024-01-17 22:40:16 +02:00
|
|
|
lres = domove("b0", 1)
|
2024-01-16 13:33:15 +02:00
|
|
|
if(lres == 2) return 2
|
|
|
|
|
else chres += lres
|
2024-01-17 22:40:16 +02:00
|
|
|
lres = domove("c0", 1)
|
2024-01-16 13:33:15 +02:00
|
|
|
if(lres == 2) return 2
|
|
|
|
|
else chres += lres
|
2024-01-17 22:40:16 +02:00
|
|
|
lres = domove("d0", 1)
|
2024-01-16 13:33:15 +02:00
|
|
|
if(lres == 2) return 2
|
|
|
|
|
else chres += lres
|
|
|
|
|
} while(chres > 0)
|
|
|
|
|
return 1 # always continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function help() {
|
2024-01-16 13:43:50 +02:00
|
|
|
print "nnfc: no-nonsense FreeCell in POSIX AWK"
|
2024-01-16 23:12:38 +02:00
|
|
|
print "Created by Luxferre in 2024, released into public domain"
|
2024-01-16 13:43:50 +02:00
|
|
|
print "Script parameters:"
|
2024-01-16 23:12:38 +02:00
|
|
|
print "-v ASCII=1\tuse ASCII instead of Unicode suits"
|
|
|
|
|
print "-v MONOCHROME=1\tdon't use colors"
|
2024-01-16 13:43:50 +02:00
|
|
|
print "-v SEED=xxxxx\tset game seed number (M$-compatible)"
|
2024-01-17 16:22:32 +02:00
|
|
|
print "Commands: q - quit, u - undo, r - restart, h - this help"
|
2024-01-16 13:43:50 +02:00
|
|
|
print "Moves: [digit][digit], where \"digits\" are:"
|
|
|
|
|
print "- 0 is foundation (can be omitted)"
|
|
|
|
|
print "- 1 to 8 are column (tableau) numbers"
|
|
|
|
|
print "- a, b, c, d are freecell IDs"
|
|
|
|
|
print "Examples:"
|
|
|
|
|
print "7a\tmove from column 7 to freecell a"
|
|
|
|
|
print "63\tmove from column 6 to column 3"
|
2024-01-16 13:49:06 +02:00
|
|
|
print "d1\tmove from freecell d to column 1"
|
2024-01-16 13:43:50 +02:00
|
|
|
print "4\tmove from column 4 to the foundation"
|
|
|
|
|
print "c\tmove from freecell c to the foundation"
|
2024-01-16 13:33:15 +02:00
|
|
|
return 1 # continue
|
2024-01-15 17:49:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BEGIN { # main code part
|
|
|
|
|
if(SEED) fc_lstate = SEED; else { # initialize PRNG
|
|
|
|
|
srand();
|
|
|
|
|
fc_lstate = int(rand() * 2146483648)
|
|
|
|
|
}
|
|
|
|
|
fc_deal = fc_lstate # save the deal number
|
|
|
|
|
asplit("A 2 3 4 5 6 7 8 9 T J Q K", cvals, " ") # card values
|
|
|
|
|
suits[0] = ASCII ? "c" : "\xe2\x99\xa3" # clubs
|
|
|
|
|
suits[1] = ASCII ? "d" : "\xe2\x99\xa6" # diamonds
|
|
|
|
|
suits[2] = ASCII ? "h" : "\xe2\x99\xa5" # hearts
|
|
|
|
|
suits[3] = ASCII ? "s" : "\xe2\x99\xa0" # spades
|
|
|
|
|
cstart[0] = MONOCHROME ? "" : "\x1b[31m" # red
|
|
|
|
|
cstart[1] = MONOCHROME ? "" : "\x1b[37m" # black (white)
|
|
|
|
|
cend = MONOCHROME ? "" : "\x1b[39m" # reset color
|
|
|
|
|
split("", usedinds) # init used indices cache
|
|
|
|
|
split("", cardstr) # init card strings array (exactly 3 char long each)
|
|
|
|
|
for(i=0;i<52;i++) { # populate it
|
|
|
|
|
si = getsuit(i) # suit index
|
|
|
|
|
cardstr[i] = cstart[getcolor(i)] sprintf("%s", cvals[i%13]) suits[si] cend " "
|
|
|
|
|
}
|
|
|
|
|
cardstr[-1] = "__ " # empty position denoted by -1 index
|
|
|
|
|
split("", seqarr) # init sequential array
|
|
|
|
|
for(i=0;i<52;i+=4) { # populate it
|
|
|
|
|
seqarr[i] = int(i/4)
|
|
|
|
|
seqarr[i+1] = seqarr[i]+13
|
|
|
|
|
seqarr[i+2] = seqarr[i]+26
|
|
|
|
|
seqarr[i+3] = seqarr[i]+39
|
|
|
|
|
}
|
|
|
|
|
rowidx = 0 # deal row index
|
|
|
|
|
colidx = 0 # deal column index
|
|
|
|
|
split("", table) # init tableau array
|
|
|
|
|
while((slen = alen(seqarr)) > 0) { # populate it
|
|
|
|
|
idx = fclcg() % slen # get the index to select
|
|
|
|
|
table[colidx++, rowidx] = seqarr[idx] # get the selected card value
|
|
|
|
|
seqarr[idx] = seqarr[slen - 1] # copy the last card to the index
|
|
|
|
|
delete seqarr[slen - 1] # shrink the array
|
|
|
|
|
if(colidx > 7) {rowidx++; colidx=0} # wrap around the deal columns
|
|
|
|
|
}
|
|
|
|
|
# init freecells and foundations arrays
|
|
|
|
|
fcell[0] = fnd[0] = -1
|
|
|
|
|
fcell[1] = fnd[1] = -1
|
|
|
|
|
fcell[2] = fnd[2] = -1
|
|
|
|
|
fcell[3] = fnd[3] = -1
|
|
|
|
|
# init tableu length tracker array
|
|
|
|
|
asplit("7 7 7 7 6 6 6 6", tablens, " ")
|
|
|
|
|
# init undo array and index
|
|
|
|
|
undos["table",0] = assocser(table, ",")
|
|
|
|
|
undos["fnd",0] = assocser(fnd, ",")
|
|
|
|
|
undos["fcell",0] = assocser(fcell, ",")
|
|
|
|
|
undos["tablens",0] = assocser(tablens, ",")
|
|
|
|
|
undoidx = 0 # the current undo index is 0
|
|
|
|
|
# first-time display
|
2024-01-16 13:33:15 +02:00
|
|
|
print "nnfc by Luxferre\nenter h for command help"
|
2024-01-15 17:49:11 +02:00
|
|
|
render()
|
|
|
|
|
printf("\n> ")
|
|
|
|
|
while((getline cmd) > 0) { # main interactive loop
|
|
|
|
|
cmd = tolower(cmd) # accept both uppercase and lowercase
|
|
|
|
|
if(cmd == "q") {print "Quitting..."; break}
|
2024-01-16 13:33:15 +02:00
|
|
|
else if(cmd == "h") res = help()
|
2024-01-17 16:22:32 +02:00
|
|
|
else if(cmd == "u") undo()
|
|
|
|
|
else if(cmd == "r") { # restart logic
|
|
|
|
|
while(undoidx > 0) undo()
|
|
|
|
|
undomoves = 0
|
|
|
|
|
} else { # pass the command to the logic function
|
|
|
|
|
undomoves = 0 # set global undo move counter
|
2024-01-17 22:40:16 +02:00
|
|
|
res = domove(cmd, 0) # manual move
|
2024-01-16 13:33:15 +02:00
|
|
|
if(res == 0) print "Invalid move!"
|
2024-01-17 22:40:16 +02:00
|
|
|
else if(res < 2) res = autohome()
|
2024-01-16 13:33:15 +02:00
|
|
|
}
|
2024-01-15 17:49:11 +02:00
|
|
|
render()
|
|
|
|
|
if(res == 2) {print "Victory!"; break}
|
|
|
|
|
printf("\n> ")
|
|
|
|
|
}
|
|
|
|
|
}
|