Programmera ett Tic Tac Toe-spel

Programmering av datorspel kan vara det mest tekniskt utmanande (och kanske det bäst betalande) jobbet som en programmerare kan ha. Spel på toppnivå kräver det bästa från både programmerare och datorer.

Visual Basic 6 har nu gått förbi som en plattform för spelprogrammering. (Det var aldrig riktigt en. Även under "goda olldagar" skulle seriösa spelprogrammerare aldrig använda ett högnivåspråk som VB 6 eftersom du bara inte kunde få den banbrytande prestanda som de flesta spel kräver.) Men det enkla "Tic Tac Toe" -spelet är en bra introduktion till programmering som är lite mer avancerad än "Hello World!"

Detta är en bra introduktion till många av de grundläggande koncepten för programmering eftersom det kombinerar tekniker inklusive:

  • Användningen av matriser. X- och O-markörerna hålls i separata matriser och hela matriserna skickas mellan funktioner för att hålla reda på spelets framsteg.
  • Använda VB 6-nivågrafik: VB 6 erbjuder inte bra grafisk kapacitet, men spelet är en bra introduktion till vad som är tillgängligt. Mycket av resten av denna serie är en utforskning av hur GDI +, nästa generation av Microsoft-grafik, ersätter VB 6-nivågrafiken.
  • Använda matematiska beräkningar för programstyrning: Programmet använder smart modulo (Mod) och heltalsberäkningar med två-spelmarkörarrayerna för att bestämma när en tre-element "win" har inträffat.

Klassen för programmering i denna artikel är kanske bara lite förbi början men det borde vara bra för "mellanliggande" programmerare. Men låt oss börja på en grundläggande nivå för att illustrera några av koncepten och komma igång med din Visual Basic-spelprogrammeringskarriär. Även studenter mer avancerade än så kan uppleva att det är lite utmanande att få objekten i form precis rätt.

Hur man spelar Tic Tac Toe

Om du aldrig har spelat Tic Tac Toe, här är reglerna. Två spelare växlar om varandra när de placerar Xs och Os i 3 x 3 spelplan.

Innan spelet börjar måste båda spelarna komma överens om vem som ska gå först och vem kommer att markera sina drag med vilken symbol. Efter det första draget placerar spelarna växelvis sina märken i någon tom cell. Målet med spelet är att vara den första spelaren med tre markeringar i en horisontell, diagonal eller vertikal linje. Om det inte finns några tomma celler och ingen av spelarna har en vinnande kombination är spelet oavgjort.

Starta programmet

Innan du startar någon faktisk kodning är det alltid en bra idé att ändra namnen på alla komponenter du använder. När du börjar kodningen kommer namnet att användas automatiskt av Visual Basic så att du vill att det ska vara rätt namn. Vi använder formulärnamnet frmTicTacToe och vi kommer också att ändra bildtexten till "Om Tic Tac Toe."

Använd formuläret, använd radverktygslådan för att rita ett 3 x 3 rutnät. Klicka på radverktyget och rit sedan en linje där du vill ha den. Du måste skapa fyra rader på detta sätt och justera deras längd och position för att få dem att se rätt ut. Visual Basic har också några praktiska verktyg under Format-menyn som hjälper. Detta är en stor chans att träna med dem.

Förutom det spelande rutnätet behöver vi några objekt för X- och O-symbolerna som kommer att placeras på rutnätet. Eftersom det finns nio utrymmen i rutnätet, skapar vi ett objektuppsättning med nio mellanslag, kallade element i Visual Basic.

Det finns flera sätt att göra nästan allt i Visual Basic-utvecklingsmiljön, och att skapa kontrollmatriser är inget undantag. Förmodligen är det enklaste sättet att skapa den första etiketten (klicka och rita precis som radverktyget), namnge den, ställa in alla attribut (som Font och ForeColor) och sedan kopiera den. VB 6 frågar om du vill skapa en kontrollgrupp. Använd namnet lblPlayGround för den första etiketten.

För att skapa de andra åtta elementen i rutnätet, välj det första etikettobjektet, ställ in indexegenskapen till noll och tryck på CTRL + C (kopia). Nu kan du trycka på CTRL + V (klistra in) för att skapa ett annat etikettobjekt. När du kopierar objekt som detta kommer varje kopia att ärva alla egenskaper utom Index från den första. Index kommer att öka med en för varje kopia. Detta är en kontrolluppsättning eftersom de alla har samma namn, men olika indexvärden.

Om du skapar matrisen på detta sätt staplas alla kopior ovanpå varandra i det övre vänstra hörnet av formuläret. Dra varje etikett till en av de spelande rutnätspositionerna. Se till att indexvärden är sekventiella i rutnätet. Programmets logik beror på det. Etikettobjektet med indexvärde 0 bör vara i det övre vänstra hörnet och den nedre högra etiketten ska ha index 8. Om etiketterna täcker det spelande rutnätet, välj varje etikett, högerklicka och välj Skicka till baksida.

Eftersom det finns åtta möjliga sätt att vinna spelet, behöver vi åtta olika linjer för att visa vinsten på det spelande rutnätet. Du kommer att använda samma teknik för att skapa en annan kontrollgrupp. Rita först linjen, namnge den linWin och ställ in indexegenskapen till noll. Använd sedan copy-paste-teknik för att producera sju rader till. Följande illustration visar hur du ställer in indexnumren korrekt.

Förutom etiketten och radobjekten behöver du några kommandoknappar för att spela spelet och fler etiketter för att behålla poängen. Stegen för att skapa dessa är inte detaljerade här, men det är de objekt du behöver.

Två knappobjekt:

  • cmdNewGame
  • cmdResetScore

Ramobjekt fraPlayFirst som innehåller två alternativsknappar:

  • optXPlayer
  • optOPlayer

Ramobjekt fraScoreBoard som innehåller sex etiketter. Endast lblXScore och lblOScore ändras i programkoden.

  • lblX
  • lblXScore
  • lblO
  • lblOScore
  • lblMinus
  • lblColon

Slutligen behöver du också etikettobjektet lblStartMsg för att 'maskera' cmdNewGame-knappen när det inte ska klickas. Detta är inte synligt i bilden nedan eftersom det upptar samma utrymme i formen som kommandoknappen. Du kanske måste flytta kommandoknappen tillfälligt för att rita den här etiketten på formuläret.

Hittills har ingen VB-kodning gjorts, men vi är äntligen redo att göra det.

initiering

Nu får du äntligen börja koda programmet. Om du inte redan har gjort det, kanske du vill ladda ner källkoden för att följa med eftersom programmets funktion förklaras.

Ett av de första designbesluten att fatta är hur man kan hålla reda på det aktuella ”läget” i spelet. Med andra ord, vad är de nuvarande Xs och Os på det spelande rutnätet och vem går vidare. Begreppet "tillstånd" är kritiskt i mycket programmering, och särskilt är det viktigt för att programmera ASP och ASP.NET för webben

Det finns flera sätt att göra detta, så det är ett kritiskt steg i analysen. Om du löste detta problem på egen hand kanske du vill rita ett flödesschema och prova olika alternativ med "repapapper" innan du startar någon kodning.

variabler

Vår lösning använder två "tvådimensionella matriser" eftersom det hjälper till att hålla reda på "tillstånd" genom att helt enkelt ändra arrayindex i programslingor. Tillståndet för det övre vänstra hörnet kommer att vara i matriselementet med index (1, 1), det övre högra hörnet är i (1, 3), det nedre höger i (3,3), och så vidare . De två matriserna som gör detta är:

iXPos (x, y)

och

iOPos (x, y)

Det finns många olika sätt detta kan göras och den slutliga VB.NET-lösningen i den här serien visar hur du gör det med bara en enda dimensionell matris.

Programmeringen för att översätta dessa matriser till spelare vinner beslut och synliga skärmar i formuläret finns på nästa sida.

Du behöver också några globala variabler enligt följande. Observera att dessa finns i koden Allmänt och deklarationer för formuläret. Detta gör dem till "modulenivå" -variabler som kan hänvisas till var som helst i koden för detta formulär. Mer information om detta finns i Förstå omfattningen av variabler i Visual Basic Hjälp.

Det finns två områden där variabler initialiseras i vårt program. Först initialiseras några variabler medan formen frmTicTacToe laddas.

Privat subform_Load ()

För det andra, innan varje nytt spel, tilldelas alla variabler som måste återställas till startvärden i en initialiseringssubutine.

Sub InitPlayGround ()

Observera att initialiseringen av formbelastningen också kallar initiering av lekplatsen.

En av programmerarens kritiska färdigheter är förmågan att använda felsökningsfaciliteter för att förstå vad koden gör. Du kan använda det här programmet för att prova:

  • Gå igenom koden med F8-tangenten
  • Ställa in en klocka på viktiga variabler, till exempel sPlaySign eller iMove
    Ställa in en brytpunkt och fråga om värdet på variabler. Till exempel i den inre slingan för initialiseringen:
lblPlayGround ((i - 1) * 3 + j - 1). Bildtext = ""

Observera att detta program tydligt visar varför det är en bra programmeringspraxis att hålla data i matriser när det är möjligt. Om du inte hade matriser i det här programmet, skulle du behöva skriva kod något liknande:

Line0.Visible = Falsk
Line1.Visible = Falsk
Line2.Visible = Falsk
Line3.Visible = Falsk
Line4.Visible = Falsk
Line5.Visible = Falsk
Line6.Visible = Falsk
Line7.Visible = Falsk

istället för det här:

För i = 0 till 7
linWin (i). Visible = Falsk
Nästa i

Göra ett drag

Om någon del av systemet kan betraktas som 'hjärtat', är det subroutine lblPlayGround_Click. Denna subroutine kallas varje gång en spelare klickar på spelningsnätet. (Klick måste vara inne i ett av de nio lblPlayGround-elementen.) Lägg märke till att den här subroutinen har ett argument: (Index som heltal). De flesta andra ”händelsunderrutiner”, som cmdNewGame_Click (), gör det inte. Index indikerar vilket etikettobjekt som har klickats på. Till exempel skulle index innehålla värdet noll för det övre vänstra hörnet av rutnätet och värdet åtta för det nedre högra hörnet.

När en spelare klickar på en kvadrat i spelrutnätet, "kommandoknappen för att starta ett annat spel, cmdNewGame," slås på "genom att göra det synligt. Tillståndet för denna kommandoknapp gör dubbelt arbete eftersom det också används som en boolesk beslutsvariabel senare Att använda ett fastighetsvärde som en beslutsvariabel avskräcks vanligtvis för att om det någonsin blir nödvändigt att ändra programmet (säg till exempel att göra kommandoknappen cmdNewGame synlig hela tiden), kommer programmet oväntat att misslyckas eftersom kanske du inte kommer ihåg att det också används som en del av programlogiken. Av detta skäl är det alltid en bra idé att söka igenom programkoden och kontrollera användningen av allt du ändrar när du gör programunderhåll, även fastighetsvärden. Detta program bryter mot regel dels för att göra detta och dels för att detta är en relativt enkel kod där det är lättare att se vad som görs och undvika problem senare.

Ett spelarval av ett spelkvadrat behandlas genom att kalla GamePlay-underprogrammet med Index som argument.

Bearbetar flytten

Först kontrollerar du om du har klickat på en ledig fyrkant.

Om lblPlayGround (xo_Move) .Caption = "" Sedan

När vi är säker på att det här är ett legitimt drag stegs räknare (iMove). De nästa två raderna är väldigt intressanta eftersom de översätter koordinaterna från den endimensionella If lblPlayGround-komponentgruppen till tvådimensionella index som du kan använda i antingen iXPos eller iOPos. Mod- och heltalsdelning ('backslash') är matematiska operationer som du inte använder varje dag, men här är ett bra exempel som visar hur de kan vara mycket användbara.

 Om lblPlayGround (xo_Move) .Caption = "" Sedan
iMove = iMove + 1
x = Int (xo_Move / 3) + 1
y = (xo_Move Mod 3) + 1

Xo_Move-värdet 0 kommer att översättas till (1, 1), 1 till (1, 2) ... 3 till (2, 1) ... 8 till (3, 3).

Värdet i sPlaySign, en variabel med modulomfång, håller reda på vilken spelare som flyttade. När rörelsearrayerna har uppdaterats kan etikettkomponenterna i det spelande rutnätet uppdateras med rätt tecken.

Om sPlaySign = "O" Sedan
iOPos (x, y) = 1
iWin = CheckWin (iOPos ())
Annan
iXPos (x, y) = 1
iWin = CheckWin (iXPos ())
Sluta om
lblPlayGround (xo_Move) .Caption = sPlaySign

När till exempel X-spelaren klickar i det övre vänstra hörnet av rutnätet har variabler följande värden:

Användarskärmen visar bara ett X i den övre vänstra rutan, medan iXPos har en 1 i den övre vänstra rutan och 0 i alla de andra. IOPos har 0 i varje ruta.

Värdena ändras när O-spelaren klickar på rutans mitt kvadrat. Nu visar iOPos en 1 i mittrutan medan användarskärmen visar ett X i det övre vänstra och ett O i mittrutan. IXPos visar endast 1 i det övre vänstra hörnet, med 0 i alla de andra rutorna.

Nu när du vet var en spelare klickade och vilken spelare som klickade (med värdet i sPlaySign), behöver du bara ta reda på om någon vann ett spel och ta reda på hur du visar det på skärmen.

Hitta en vinnare

Efter varje drag kontrollerar CheckWin-funktionen efter den vinnande kombinationen. CheckWin fungerar genom att lägga till varje rad, över varje kolumn och genom varje diagonal. Att spåra stegen genom CheckWin med Visual Basic: s felsökningsfunktion kan vara mycket lärorikt. Att hitta en vinst är en fråga om först, kontrollera om tre 1 hittades i var och en av de enskilda kontrollerna i variabeln iScore och sedan returnera ett unikt "signatur" -värde i Checkwin som används som matrisindex för att ändra den synliga egenskapen för ett element i linWin-komponentgruppen. Om det inte finns någon vinnare kommer CheckWin att innehålla värdet -1. Om det finns en vinnare, uppdateras displayen, resultattavlan ändras, ett gratuleringsmeddelande visas och spelet startas om.

Låt oss gå igenom en av kontrollerna i detalj för att se hur det fungerar. De andra är likadana.

"Kontrollera raderna för 3
För i = 1 till 3
iScore = 0
CheckWin = CheckWin + 1
För j = 1 till 3
iScore = iScore + iPos (i, j)
Nästa j
Om iScore = 3 Sedan
Avsluta funktion
Sluta om
Nästa i

Det första att märka är att den första indexräknaren i räknar ner raderna medan den andra j räknar över kolumnerna. Den yttre slingan går sedan helt enkelt från en rad till nästa. Den inre slingan räknar 1: erna i den aktuella raden. Om det finns tre, har du en vinnare.

Lägg märke till att du också håller reda på det totala antalet rutor som testats i variabeln CheckWin, vilket är värdet som returneras när denna funktion avslutas. Varje vinnande kombination slutar med ett unikt värde i CheckWin från 0 till 7 som används för att välja ett av elementen i komponentgruppen linWin (). Detta gör ordningen på koden i funktionen CheckWin också viktig! Om du flyttade ett av blocken med loopkod (som ovan), skulle fel linje dras på det spelande rutnätet när någon vinner. Prova och se!

Efterbehandlingsdetaljer

Den enda koden som ännu inte diskuterats är subrutinen för ett nytt spel och subrutinen som kommer att återställa poängen. Resten av logiken i systemet gör det lätt att skapa dessa. För att starta ett nytt spel, måste du bara ringa InitPlayGround-subrutinen. Som en bekvämlighet för spelare eftersom knappen kan klickas i mitten av ett spel ber du om bekräftelse innan du går vidare. Du ber också om bekräftelse innan du startar om resultattavlan.