This script is also mainly a showoff of heavy object/OOP usage (including a QuickSort implementation in AHK, really crappy i18n that I quickly hacked in so that some friends who don't speak English could try the script, a functor class that implements comparison with state, some class inheritance with "virtual" methods as well as some really basic AI), and has a terrible user interface consisting of MsgBoxes and InputBoxes (I couldn't be arsed to make a cool GUI, I leave that as an exercise for the reader). So here's the unedited rawness of the script:Wikipedia wrote:Sevens, also known as Laying Out Sevens, Fan Tan, Crazy Sevens or Parliament, is a card game for 3–7 players using a standard deck of 52 cards. Cards are played out to form a layout of sequences going up and down in suit from the sevens (as in many solitaire games). The game is won by emptying one's hand before the other players.
Code: Select all
#NoEnv
#NoTrayIcon
#SingleInstance Ignore
global g_allStrings := { _:_
, en: { _:_
, nameEnter: "Please enter your name:"
, cpuEnter: "Please enter the number of CPUs:"
, finish: " -- FINISHES as #"
, output: " outputted the "
, pass: " passed"
, ply_5o: "You have 5 de Oros, therefore you go first"
, ply_pass: "You cannot play any card, therefore you pass"
, ply_sel1: "Select a card..."
, ply_sel2: "Illegal option, please select a valid card..."
, cancel: "The game is canceled"
, ranking: "Player ranking" }
, sp: { _:_
, nameEnter: "Introduzca su nombre:"
, cpuEnter: "Introduzca el número de CPUs:"
, finish: " -- TERMINA en el lugar nº"
, output: " echó el "
, pass: " pasa"
, ply_5o: "Tienes el 5 de Oros así que vas primero"
, ply_pass: "No puedes echar ninguna carta así que pasas"
, ply_sel1: "Elige una carta..."
, ply_sel2: "Opción ilegal, elige una carta válida..."
, cancel: "Juego cancelado"
, ranking: "Clasificación" } }
if (A_Language & 0xFF) = 0x0a
lang := "sp"
else
lang := "en"
global g_strings := g_allStrings[lang]
InputBox, theName,, % g_strings.nameEnter,,,,,,,, %A_UserName%
if ErrorLevel
ExitApp
InputBox, nPlayers,, % g_strings.cpuEnter,,,,,,,, 1
if ErrorLevel
ExitApp
nPlayers++
players := []
game := new Cinquillo
winList := []
Random, humanId, 1, %nPlayers%
cpup := 0
Loop, % nPlayers
{
ply := A_Index = humanId ? new PlayerHuman(game,theName) : new PlayerCPU(game,"CPU #" (++cpup))
players.Insert(ply)
}
; Deal cards
Loop
{
curP := Mod(A_Index-1, nPlayers)+1
card := game.DealCard()
if !card
break
players[curP].ReceiveCard(card)
}
; Play the game
while ObjCount(winList) != nPlayers
{
curP := players[Mod(A_Index-1, nPlayers)+1]
if curP.Finished()
continue
card := curP.Play()
if card
{
if curP.Finished()
{
winList.Insert(curP.name)
winText := g_strings.finish ObjCount(winList)
} else
winText := ""
MsgBox % curP.name g_strings.output card.ToString() winText "`n`n" game.State()
} else if !game.Beginning()
MsgBox % curP.name g_strings.pass "`n`n" game.State()
}
winText := ""
for k,v in winList
winText .= "#" k " - " v "`n"
MsgBox % g_strings.ranking "`n" winText
class Card
{
__New(id,suit)
{
this.i := id, this.s := suit
}
ToString()
{
return this.i " de " this.s
}
ToString2()
{
return this.i SubStr(this.s, 1, 1)
}
}
class Cinquillo
{
desk := {}
d := Deck()
Beginning()
{
return !this.desk.Oros
}
DealCard()
{
return ObjRemove(this.d)
}
PlayCard(card, upd := true)
{
if !this.desk.Oros && !(card.i = 5 && card.s = "Oros")
return false
if card.i = 5
{
if upd
this.desk[card.s] := { min: 5, max: 5 }
return true
} else
{
if not x := this.desk[card.s]
return false
d := card.i
if (d < 5) && x.min == (d+1)
{
if upd
x.min := d
return true
} else if x.max == (d-1)
{
if upd
x.max := d
return true
}
return false
}
}
State()
{
for k,x in this.desk
out .= k ": <" x.min "," x.max ">`n"
StringTrimRight, out, out, 1
return out
}
}
class Player
{
cards := []
__New(cinq, name)
{
this.g := cinq
this.name := name
}
ReceiveCard(card)
{
this.cards.Insert(card)
}
Play()
{
throw Exception("Pure virtual method called")
}
State()
{
for _,c in this.cards
x .= c.ToString2() " "
StringTrimRight, x, x, 1
return x
}
Finished()
{
return !ObjCount(this.cards)
}
}
class PlayerHuman extends Player
{
Play()
{
if this.Finished()
return
if this.g.Beginning()
{
for k,c in this.cards
{
if (c.i = 5 && c.s = "Oros")
{
MsgBox % "(" this.name "): " g_strings.ply_5o
ObjRemove(this.cards, k)
this.g.PlayCard(c)
return c
}
}
return
}
vv := false
for k,c in this.cards
if this.g.PlayCard(c,false)
{
vv := true
break
}
if !vv
{
MsgBox % "(" this.name "): " g_strings.ply_pass
return
}
QuickSort(this.cards, Func("BasicCardComparator"))
Loop
{
ill := A_Index > 1 ? g_strings.ply_sel2 : g_strings.ply_sel1
prompt := "(" this.name "): " ill "`n" this.State() "`n`n" this.g.State()
InputBox, p,, %prompt%,,, 266
if ErrorLevel
throw Exception(g_strings.cancel)
if !RegExMatch(p, "i)^(\d+)([OCBE])$", o)
continue
for k,c in this.cards
{
if (c.i = o1 && SubStr(c.s,1,1) = o2)
{
if this.g.PlayCard(c)
{
ObjRemove(this.cards, k)
return c
}
continue 2
}
}
}
}
}
BasicCardComparator(c1, c2)
{
static _ := { Oros: 0, Copas: 1, Bastos: 2, Espadas: 3 }
c1i := c1.i, c2i := c2.i, c1s := c1.s, c2s := c2.s
if (c1s != c2s)
return _[c1s] - _[c2s]
return c1i-c2i
}
class PlayerCPU extends Player
{
Play()
{
if this.Finished()
return
comp := new CardComp(CardPrioInfo(this.cards))
QuickSort(this.cards, comp)
for k,c in this.cards
if this.g.PlayCard(c)
{
ObjRemove(this.cards, k)
return c
}
return
}
}
QuickSort(arr, comp, i := -1, j := -1)
{
if (i = -1) || (j = -1)
i := 1, j := ObjCount(arr)+1
if (j-i) < 2
return
; Select pivot
Random, id, % i, % j-1
; Dutch flag
r := DutchFlag(arr, comp, i, j, id)
; Recursive
QuickSort(arr, comp, i, r[1])
QuickSort(arr, comp, r[2], j)
}
DutchFlag(arr, comp, i, j, pivId)
{
a := i, b := i+1, c := j
ObjSwap(arr, a, pivId)
while ((c-b) > 0)
{
res := %comp%(arr[b], arr[a])
if (res < 0)
ObjSwap(arr, a, b), a++, b++
else if (res = 0)
b++
else
ObjSwap(arr, b, c-1), c--
}
return [a,b]
}
ObjSwap(x,i1,i2)
{
r := x[i1], x[i1] := x[i2], x[i2] := r
}
ObjCount(x)
{
r := ObjMaxIndex(x)
return r ? r : 0
}
class CardComp
{
__New(relat)
{
this.r := relat
}
__Call(m, c1, c2)
{
r := this.r
return CardPrio(r,c1) - CardPrio(r,c2)
}
}
CardPrioInfo(list)
{
out := { Oros: { max: -1, has5: false, cardC: 0 }
, Copas: { max: -1, has5: false, cardC: 0 }
, Bastos: { max: -1, has5: false, cardC: 0 }
, Espadas: { max: -1, has5: false, cardC: 0 } }
for _,c in list
{
d := out[this.s]
d.cardC++
if c.i = 5
d.has5 := true
curD := Abs(c.i-5)
if (curD > d.maxD)
d.maxD := curD
}
return out
}
CardPrio(info, card)
{
dist := Abs(card.i-5), d := info[card.s]
return d.has5 ? (dist+d.maxD+d.cardC) : (dist*2+d.cardC)
}
Deck()
{
ret := {}
for _,d in ["Oros","Copas","Bastos","Espadas"]
Loop 12
ret.Insert(new Card(A_Index,d))
return ObjShuffle(ret)
}
ObjToString(obj)
{
for k,v in obj
x .= "[" k "]: " v.ToString() "`n"
StringTrimRight, x, x, 1
return x
}
ObjShuffle(obj)
{
Loop, % len := ObjMaxIndex(obj)
{
i := len-A_Index+1
Random, j, 1, % i
x := obj[j], obj[j] := obj[i], obj[i] := x
}
return obj
}