Neben Würfeln, welches das erste Paket ist, dass ich hier auf der Webseite gezeigt habe sind Spielkarten eines der am breitesten einsetzbaren Objekte um Spiel zu programmieren. Neben dem üblichen, wie Poker, Black Jack , Skat, Doppel Kopf, Romme, Gin, MauMau usw. kann man mit Karten natürlich auch andere Sachen machen wie ein Memory programmieren.
In dem Video zeige ich den Code, so weit wie ich ihn bis jetzt programmiert habe. Erkläre meine Gedanken hinter manchen Entscheidungen und verbessere noch mal die SetzeKarte-Funktion.
Das Highlighting ist jetzt auch implementiert, sieht aber doch etwas seltsam aus bei den Karten. Kann man vielleicht auch anders machen. Ich hatte ja schon erwähnt, dass dies häufig mit gutem Grund durch die Veränderung der Position passiert. Für diese Implementierung seit ja dann selbst verantwortlich.
Für die richtige Funktion der Karten ist leider das Paket Zufallszahl notwendig (nicht veröffentlicht – daher mir eine Mail schreiben), von meinem Dozenten Herr Schmitt, so wie sein Paket gfx, welches aber so wohl für Linux als auch für Windows hier bezogen werden kann.
Des weiteren habe ich den Font Lato von Google verwendet. Einfach dem Link folgen und die Dateiinhalte entpacken und unter go>fonts abspeichern.
Dann sollte der Verwendung der Spielkarten nichts im Wege stehen. Sollte es doch Probleme geben, verweise ich auf den Link zu meiner Mail.
Aufgaben:
Vervollständigen Sie das Interface und implementieren Sie folgende Funktionen (Implementierung & Interface).
- Interface
- Voraussetzung und Effekt/Ergebnis ausformulieren (auch die Neuen!)
- Seter- u. Geter-Funktionen
- SetzeHighlightFarbe
- SetzeSuitFarbe(suit string, r,g,b uint8) //Achtung: Suit-Auswahl!!
- SetzeWertFarbe(r,g,b uint8)
- SetzeDeckFarbe(r,g,b uint8)
- GibGröße () (size uint16)
- GibPosition () (x,y uint16)
Code (so weit implementiert)
Implementierung
package karten
import (
"gfx"
. "zufallszahlen"
"strconv")
type data struct {
wert uint8 //Zahlenwert wird gespeichert 2,3,4,5,6,7,8,9,10,11 = B, 12 = D, 13 = K, 1 = A
suit uint8 //0=Karo,1=Herz,2=Pik,3=Kreuz
x,y,size uint16 //Position & Größe
sr,sg,sb uint8 //Farbe für den Suit
lr,lg,lb uint8 //Farbe für die Buchstaben (Letter)30,144,255
hr,hg,hb uint8 //Farbe fürs Highlighting
dr,dg,db uint8 //Farbe fürs Deck - Farbe der verdeckten Karte
aufgedeckt bool //True wenn die Seite mit den Werten sichtbar ist
highlighting bool //True wenn Highlighting an ist
}
func New () *data {
var k *data
k = new(data)
Randomisieren()
(*k).wert = uint8(Zufallszahl(1,13)) //Wählt zufällig eine Karte aus - Wert & Suit
(*k).suit = uint8(Zufallszahl(0,3))
if (*k).suit<2 {
(*k).sr,(*k).sg,(*k).sb = 255,0,0 //Setz die Default-Farbe für die Suits
(*k).lr,(*k).lg,(*k).lb = 255,0,0
} else {
(*k).sr,(*k).sg,(*k).sb = 0,0,0
(*k).lr,(*k).lg,(*k).lb = 0,0,0
}
(*k).dr,(*k).dg,(*k).db = 30,144,255 //Setzt die Default-Farbe für das Deck
(*k).hr,(*k).hg,(*k).hb = 212,8,8 //Setzt die Default-Farbe fürs Highlighting
return k
}
func (k *data) GibWert () string {
switch (*k).wert {
case 11:
return "B"
case 12:
return "D"
case 13:
return "K"
case 1:
return "A"
default:
var sWert string
sWert = strconv.Itoa(int((*k).wert))
return sWert
}
}
func (k *data) GibSuit () string {
var erg string
switch (*k).suit {
case 0:
erg = "Karo"
case 1:
erg = "Herz"
case 2:
erg = "Pik"
case 3:
erg = "Kreuz"
}
return erg
}
func (k *data) GibWertFarbe () (r,g,b uint8) {
return (*k).lr,(*k).lg,(*k).lb
}
func (k *data) GibSuitFarbe () (r,g,b uint8) {
return (*k).sr,(*k).sg,(*k).sb
}
func (k *data) GibHighlightFarbe() (r,g,b uint8) {
return (*k).hr,(*k).hg,(*k).hb
}
func (k *data) SetzeHighlight (highlight bool) {
(*k).highlighting = highlight
}
func (k *data) SetzeKarte (wert, suit string) (verändert bool) {
var vwert,vsuit bool
switch suit {
case "Karo":
(*k).suit = 0
(*k).lr,(*k).lg,(*k).lb = 255,0,0
(*k).sr,(*k).sg,(*k).sb = 255,0,0
vsuit = true
case "Herz":
(*k).suit = 1
(*k).lr,(*k).lg,(*k).lb = 255,0,0
(*k).sr,(*k).sg,(*k).sb = 255,0,0
vsuit = true
case "Pik":
(*k).suit = 2
(*k).lr,(*k).lg,(*k).lb = 0,0,0
(*k).sr,(*k).sg,(*k).sb = 0,0,0
vsuit = true
case "Kreuz":
(*k).suit = 3
(*k).lr,(*k).lg,(*k).lb = 0,0,0
(*k).sr,(*k).sg,(*k).sb = 0,0,0
vsuit = true
}
switch wert {
case "A":
(*k).wert = 1
vwert = true
case "K":
(*k).wert = 13
vwert = true
case "D":
(*k).wert = 12
vwert = true
case "B":
(*k).wert = 11
vwert = true
default:
erg,err:=strconv.Atoi(wert)
if err == nil && erg>=2 && erg<=10 {
(*k).wert = uint8(erg)
vwert = true
}
}
return vwert && vsuit
}
func (k *data) Umdrehen () {
if (*k).aufgedeckt {
(*k).aufgedeckt = false
} else {
(*k).aufgedeckt = true
}
}
func (k *data) Aufdecken () {
(*k).aufgedeckt = true
}
func (k *data) Zudecken () {
(*k).aufgedeckt = false
}
func (k *data) String () string {
var erg string
if (*k).wert != 10 {
erg = erg + " "
}
erg = erg + k.GibWert()
erg = erg + " "
erg = erg + k.GibSuit()
if (*k).suit!=3 {
switch (*k).suit {
case 2:
erg = erg + " "
default:
erg = erg + " "
}
}
return erg
}
func kreuz (x,y,size uint16) {
gfx.Vollrechteck(x+size/3,y,size/3,size)
gfx.Vollrechteck(x,y+size/3,size,size/3)
}
func karo (x,y,size uint16) {
gfx.Volldreieck(x+size/2,y,x+size/12,y+size/2,x+size*9/12,y+size/2)
gfx.Volldreieck(x+size/2,y+size,x+size*3/12,y+size/2,x+size*11/12,y+size/2)
}
func herz (x,y,size uint16) {
gfx.Volldreieck(x+size/3,y,x+size/12,y+size/3,x+size/2,y+size/3)
gfx.Volldreieck(x+size*2/3,y,x+size*11/12,y+size/3,x+size/2,y+size/3)
gfx.Volldreieck(x+size/12,y+size/3,x+size/2,y+size,x+size/2,y+size/3)
gfx.Volldreieck(x+size*11/12,y+size/3,x+size/2,y+size,x+size/2,y+size/3)
}
func pik (x,y,size uint16) {
gfx.Volldreieck(x+size/6,y,x+size/6,y+size*7/12,x+size*5/6,y+size/2-size/12)
gfx.Volldreieck(x+size/6,y+size*5/12,x+size/6,y+size,x+size*5/6,y+size-size/12)
}
func (k *data) GehörtPunktzurKarte (xp,yp uint16) bool {
var xk,yk,size uint16
xk = (*k).x
yk = (*k).y
size = (*k).size
//Vollrechteck(x-1,y-1,2*size+2,3*size+2)
if xp>=xk-1 && xp<=xk-1+2*size+2 && yp>=yk-1 && yp<=yk-1+3*size+2 {
return true
}
//Vollrechteck(x-size/10-1,y-1,size/10+2,3*size+2)
if xp>=xk-size/10-1 && xp<=xk-size/10-1+size/10+2 && yp>=yk-1 && yp<=yk-1+3*size+2{
return true
}
//Vollrechteck(x-1,y-size/10-1,2*size+2,size/10+2)
if xp>=xk-1 && xp<=xk-1+2*size+2 && yp>=yk-size/10-1 && yp<=yk-size/10-1+size/10+2 {
return true
}
//Vollrechteck(x+size*2-1,y,size/10+1+2,3*size+2)
if xp>=xk+size*2-1 && xp<=xk+size*2-1+size/10+1+2 && yp>=yk && yp<=yk+3*size+2 {
return true
}
//Vollrechteck(x-1,y+3*size-1,size*2+2,size/10+1+2)
if xp>=xk-1 && xp<=xk-1+size*2+2 && yp>=yk+3*size-1 && yp<=yk+3*size-1+size/10+1+2 {
return true
}
//Vollkreis(x-1,y-1,size/10)
if (int(xp)-int(xk-1))*(int(xp)-int(xk-1))+(int(yp)-int(yk-1))*(int(yp)-int(yk-1)) <= int(size/10)*int(size/10) {
return true
}
//Vollkreis(x-1,y+3*size+1,size/10)
if (int(xp)-int(xk-1))*(int(xp)-int(xk-1))+(int(yp)-int(yk+3*size+1))*(int(yp)-int(yk+3*size+1)) <= int(size/10)*int(size/10) {
return true
}
//Vollkreis(x+size*2-1+2,y-1,size/10)
if (int(xp)-int(xk+size*2-1+2))*(int(xp)-int(xk+size*2-1+2))+(int(yp)-int(yk-1))*(int(yp)-int(yk-1)) <= int(size/10)*int(size/10) {
return true
}
//Vollkreis(x+size*2-1+2,y+3*size+1,size/10)
if (int(xp)-int(xk+size*2-1+2))*(int(xp)-int(xk+size*2-1+2))+(int(yp)-int(yk+3*size+1))*(int(yp)-int(yk+3*size+1)) <= int(size/10)*int(size/10) {
return true
}
return false
}
func (k *data) Draw (x,y,size uint16) {
(*k).x = x
(*k).y = y
(*k).size = size
if (*k).highlighting {
var hr,hg,hb uint8
hr,hg,hb = k.GibHighlightFarbe()
gfx.Stiftfarbe(hr,hg,hb)
gfx.Vollrechteck(x-3,y-3,2*size+6,3*size+6)
gfx.Vollrechteck(x-size/10-3,y-3,size/10+6,3*size+6)
gfx.Vollrechteck(x-3,y-size/10-3,2*size+6,size/10+6)
gfx.Vollrechteck(x+size*2-3,y,size/10+1+6,3*size+6)
gfx.Vollrechteck(x-3,y+3*size-3,size*2+6,size/10+1+6)
gfx.Vollkreis(x-3,y-3,size/10)
gfx.Vollkreis(x-3,y+3*size+3,size/10)
gfx.Vollkreis(x+size*2-3+6,y-3,size/10)
gfx.Vollkreis(x+size*2-3+6,y+3*size+3,size/10)
}
if (*k).aufgedeckt {
//Körper
gfx.Stiftfarbe(0,0,0)
gfx.Vollrechteck(x-1,y-1,2*size+2,3*size+2)
gfx.Vollrechteck(x-size/10-1,y-1,size/10+2,3*size+2)
gfx.Vollrechteck(x-1,y-size/10-1,2*size+2,size/10+2)
gfx.Vollrechteck(x+size*2-1,y,size/10+1+2,3*size+2)
gfx.Vollrechteck(x-1,y+3*size-1,size*2+2,size/10+1+2)
gfx.Vollkreis(x-1,y-1,size/10)
gfx.Vollkreis(x-1,y+3*size+1,size/10)
gfx.Vollkreis(x+size*2-1+2,y-1,size/10)
gfx.Vollkreis(x+size*2-1+2,y+3*size+1,size/10)
gfx.Stiftfarbe(255,255,255)
gfx.Vollrechteck(x,y,2*size,3*size)
gfx.Vollrechteck(x-size/10,y,size/10,3*size)
gfx.Vollrechteck(x,y-size/10,2*size,size/10)
gfx.Vollrechteck(x+size*2,y,size/10+1,3*size)
gfx.Vollrechteck(x,y+3*size,size*2,size/10+1)
gfx.Vollkreis(x,y,size/10)
gfx.Vollkreis(x,y+3*size,size/10)
gfx.Vollkreis(x+size*2,y,size/10)
gfx.Vollkreis(x+size*2,y+3*size,size/10)
//Wert
var br,bg,bb uint8
br,bg,bb = k.GibWertFarbe()
gfx.Stiftfarbe(br,bg,bb)
gfx.SetzeFont ("../fonts/Lato-Black.ttf", int(size/3))
w:=k.GibWert()
if (*k).wert==10 { //Zweistellige Zahlen (10)
gfx.SchreibeFont (x,y,w)
gfx.SchreibeFont (x+2*size-size/3-size*7/100,y+3*size-size/3-size*9/100,w)
gfx.SetzeFont ("../fonts/Lato-Black.ttf", int(size))
gfx.SchreibeFont (x+size/2-size*11/100,y+size/2+size*36/100,w)
} else if (*k).wert>10 || (*k).wert==1{ //Buchstaben
gfx.SchreibeFont (x+size*9/100,y,w)
gfx.SchreibeFont (x+2*size-size/3-size*1/100,y+3*size-size/3-size*9/100,w)
gfx.SetzeFont ("../fonts/Lato-Black.ttf", int(size))
gfx.SchreibeFont (x+size/2+size*14/100,y+size/2+size*36/100,w)
} else { //Einstellige Zahlen
gfx.SchreibeFont (x+size*12/100,y,w)
gfx.SchreibeFont (x+2*size-size/3+size*2/100,y+3*size-size/3-size*9/100,w)
gfx.SetzeFont ("../fonts/Lato-Black.ttf", int(size))
gfx.SchreibeFont (x+size/2+size*25/100,y+size/2+size*36/100,w)
}
//Suit
var sr,sg,sb uint8
sr,sg,sb = k.GibSuitFarbe()
gfx.Stiftfarbe(sr,sg,sb)
switch k.GibSuit() {
case "Karo":
karo(x+size*9/100,y+size/3+size*2/100,size/4)
karo(x-size*17/100-size/3+2*size+size/6,y-size*7/12+3*size-size*3/100,size/4)
karo(x+size-size*24/100,y+size/3+size*20/100,size/2)
karo(x+size-size*24/100,y+size*5/3+size*29/100,size/2)
case "Herz":
herz(x+size*9/100,y+size/3+size*2/100,size/4)
herz(x-size*17/100-size/3+2*size+size/6,y-size*7/12+3*size-size*3/100,size/4)
herz(x+size-size*24/100,y+size/3+size*20/100,size/2)
herz(x+size-size*24/100,y+size*5/3+size*29/100,size/2)
case "Pik":
pik(x+size*9/100,y+size/3+size*2/100,size/4)
pik(x-size*17/100-size/3+2*size+size/6,y-size*7/12+3*size-size*3/100,size/4)
pik(x+size-size*24/100,y+size/3+size*20/100,size/2)
pik(x+size-size*24/100,y+size*5/3+size*29/100,size/2)
case "Kreuz":
kreuz(x+size*9/100,y+size/3+size*2/100,size/4)
kreuz(x-size*17/100-size/3+2*size+size/6,y-size*7/12+3*size-size*3/100,size/4)
kreuz(x+size-size*24/100,y+size/3+size*20/100,size/2)
kreuz(x+size-size*24/100,y+size*5/3+size*29/100,size/2)
}
} else {
//Körper
//Rand
gfx.Stiftfarbe(0,0,0)
gfx.Vollrechteck(x-1,y-1,2*size+2,3*size+2)
gfx.Vollrechteck(x-size/10-1,y-1,size/10+2,3*size+2)
gfx.Vollrechteck(x-1,y-size/10-1,2*size+2,size/10+2)
gfx.Vollrechteck(x+size*2-1,y,size/10+1+2,3*size+2)
gfx.Vollrechteck(x-1,y+3*size-1,size*2+2,size/10+1+2)
gfx.Vollkreis(x-1,y-1,size/10)
gfx.Vollkreis(x-1,y+3*size+1,size/10)
gfx.Vollkreis(x+size*2-1+2,y-1,size/10)
gfx.Vollkreis(x+size*2-1+2,y+3*size+1,size/10)
//Fläche
gfx.Stiftfarbe(255,255,255)
gfx.Vollrechteck(x,y,2*size,3*size)
gfx.Vollrechteck(x-size/10,y,size/10,3*size)
gfx.Vollrechteck(x,y-size/10,2*size,size/10)
gfx.Vollrechteck(x+size*2,y,size/10+1,3*size)
gfx.Vollrechteck(x,y+3*size,size*2,size/10+1)
gfx.Vollkreis(x,y,size/10)
gfx.Vollkreis(x,y+3*size,size/10)
gfx.Vollkreis(x+size*2,y,size/10)
gfx.Vollkreis(x+size*2,y+3*size,size/10)
//Muster
var dr,dg,db uint8
dr,dg,db = (*k).dr,(*k).dg,(*k).db
gfx.Stiftfarbe(dr,dg,db)
gfx.Vollrechteck(x-size/10,y+size*2/12+size*1/100,size*2+2*size/10+1,size/3)
gfx.Vollrechteck(x-size/10,y+size*9/12+size*1/100,size*2+2*size/10+1,size/3)
gfx.Vollrechteck(x-size/10,y+size*16/12+size*1/100,size*2+2*size/10+1,size/3)
gfx.Vollrechteck(x-size/10,y+size*23/12+size*1/100,size*2+2*size/10+1,size/3)
gfx.Vollrechteck(x-size/10,y+size*30/12+size*1/100,size*2+2*size/10+1,size/3)
}
}
Interface
package karten
import (. "zufallszahlen")
type Karte interface {
GibWert () string
GibSuit () string
GibWertFarbe () (r,g,b uint8)
GibSuitFarbe () (r,g,b uint8)
GibHighlightFarbe() (r,g,b uint8)
SetzeKarte (wert, suit string) (verändert bool)
SetzeHighlight(highlight bool)
String () string
Umdrehen ()
Aufdecken ()
Zudecken ()
GehörtPunktzurKarte (xp,yp uint16) bool
Draw (x,y,size uint16)
}
func Deck52 () []Karte {
var deck []Karte
werte:=[13]string{"2","3","4","5","6","7","8","9","10","B","D","K","A"}
suits:=[4]string{"Karo","Herz","Pik","Kreuz"}
for i:=0;i<len(suits);i++{
for j:=0;j<len(werte);j++{
var k Karte
k = New()
k.SetzeKarte(werte[j],suits[i])
deck = append(deck,k)
}
}
return deck
}
func Deck32 () []Karte {
var deck []Karte
werte:=[8]string{"7","8","9","10","B","D","K","A"}
suits:=[4]string{"Karo","Herz","Pik","Kreuz"}
for i:=0;i<len(suits);i++{
for j:=0;j<len(werte);j++{
var k Karte
k = New()
k.SetzeKarte(werte[j],suits[i])
deck = append(deck,k)
}
}
return deck
}
func Mischen (deck []Karte) []Karte {
Randomisieren()
var ndeck []Karte
var vorne int64
for 0<len(deck) {
var k Karte
var zahl int64
zahl = Zufallszahl(0,int64(len(deck)-1))
k = deck[int(zahl)]
deck = append(deck[:int(zahl)],deck[int(zahl)+1:]...)
vorne = Zufallszahl(0,1)
if vorne==0 {
ndeck = append(ndeck,k)
} else {
var nndeck []Karte
nndeck = append(nndeck,k)
ndeck = append(nndeck,ndeck...)
}
}
return ndeck
}
func GetTopSelected (deck []Karte, x,y uint16) (treffer bool, index int) {
for i:=0;i<len(deck);i++ {
if deck[i].GehörtPunktzurKarte (x,y) {
index = i
if !treffer {
treffer = true
}
}
}
return
}
Kartentest-Programm
package main
import (
. "gfx" //der Punkt sorgt dafür, dass bei Aufrufen von Funktionen aus dem gfx-Paket nicht immer gfx.Funktionsname schreiben muss
"karten"
)
func main () {
var x,y,size uint16 //Initialisierung der Variablen für Koordinaten(x,y) und Größe der Spielkarten
x,y,size = 50,50,52 //Belegung der Kooerdinate des Starpunktes an dem die erste Karte gezeichnet werden soll + Größe
var deck []karten.Karte //Initialisierung der Variable "deck", die gleich ein 52er Kartendeck entgegen nimmt
deck = karten.Deck52() //Es wird ein sortirtes Kartendeck mit 52 Karten generiert und an die Variable "deck" übergeben
Fenster(1200,800) //Ein gfx-Fenster mit der Breite 1200 & Höhe 800 wird geöffnet
Stiftfarbe(47,186,51) //Die Stiftfarbe wird auf grün gesetzt
Vollrechteck(0,0,1200,800) //Zeichne ein grünes Rechteck in der größe des Fensters
//MausLesen1 () (taste uint8, status int8, mausX, mausY uint16) //Überreste: Wenn man auf Karten klicken können möchte braucht man var taste & status +
//var taste uint8 //Funktionsrumpf aus dem gfx-Paket, da ich mir die reihenfolge der Variablen nicht merken kann
//var status int8
var mx,my uint16 //Erzeugt zwei variablen, die die Koordinaten des Mauszeigers aufnehmen
deck = karten.Mischen(deck) //Das Deck wird gemischt
for { //Endlosschleife Anfang
_,_,mx,my = MausLesen1() //Koordinate des Mauszeigers werden ausgelesen und an die Variablen mx & my übergeben; _,_,: verwirft die Werte für taste & status, die nicht gebraucht werden
treffer,index:=karten.GetTopSelected(deck,mx,my) //Die Variablen treffer (ob der Mauszeiger auf einer Karte liegt) & index (index der obersten getroffenen Karte im Deck)
if treffer { //Wenn der Mauszeiger auf einer Karte liegt, dann...
deck[index].Aufdecken() //decke die oberste Karte auf der der Mauszeiger liegt auf,
for i:=0;i<len(deck);i++{ //alle anderen Karten
if i!=index{ //bis auf die Karte die du aufgedeckt hast,
deck[i].Zudecken() //deckst du zu.
}
}
} else { //Wenn der Mauszeiger nicht auf einer Karte liegt, dann ...
for i:=0;i<len(deck);i++{ //decke alle Karte zu.
deck[i].Zudecken()
}
}
UpdateAus() //Zeichne im Hintergrund nicht sichtbar
Stiftfarbe(255,255,255) //Setze die Stiftfarbe auf weiß
Cls() //Mach das ganze Fenster weiß
Stiftfarbe(47,186,51) //Setze die Stiftfarbe auf grün
Vollrechteck(0,0,1200,800) //Zeichne ein grünes Rechteck in der größe des Fensters
for i:=0;i<len(deck);i++{ //Zähle durch die Karten des Decks durch, und mache folgendes
deck[i].Draw(x+uint16(i)%13*size,y+uint16(i)/13*size*10/3,size) //deck[i] wählt die aktuelle Karte aus über den Index;
//Draw zeichnet die Karte
//x+uint16(i)%13*size - sorgt dafür, dass die Karten in x Richtung mit jedem i um 50px (size - ca. halbe Kartenbreite) verschoben werden.
//das passiert aber immer nur dann, wenn die Zahl mit Rest durch 13 teilbar ist, ist sie durch 13 glatt teilbar wird wieder bei 0 angefangen, das macht der Modolo-Operator %13
//Effekt: man bekommt Zeilen mit 13 Karten darin
//y+uint16(i)/13*size*10/3 - sorgt dafür, dass die Karten um 3 x 50px (volle Karten höhe) in y Richtung verschoben werden, aber nur dann, wenn die i sich durch 13 Teilen lässt (i:0-12 = 0; 13-25 = 1; 26-38 = 2; 39-52 = 3).
//Effekt: alle 13 Karten wird eine neue Zeile begonnen
//Gesamteffekt ist, dass 4 zeilen a 13 Karten auf dem Bildschirm gezeichnet werden in der Größe 50
}
UpdateAn() //Alles was im Hintergrund gezeichnet wurde wird auf einmal sichtbar - verhindert Flackern in den Bildern
} //Endlosschleife Ende
}