Een Tic Tac Toe-spel programmeren

Het programmeren van computerspellen is misschien de technisch meest uitdagende (en mogelijk de best betaalde) taak die een programmeur kan hebben. Top-level games vereisen het beste van zowel programmeurs als computers.

Visual Basic 6 is nu grondig omzeild als platform voor game-programmering. (Het was er nooit echt een. Zelfs in de "goede oude tijd" zouden serieuze spelprogrammeurs nooit een taal op hoog niveau zoals VB 6 gebruiken, omdat je gewoon niet de geavanceerde prestaties kon krijgen die de meeste games vereisen.) Maar de eenvoudige "Tic Tac Toe" -game is een geweldige introductie tot programmeren die iets geavanceerder is dan "Hallo wereld!"

Dit is een geweldige introductie tot veel van de fundamentele concepten van programmeren, omdat het technieken combineert, waaronder:

  • Het gebruik van arrays. De X- en O-markeringen worden in afzonderlijke arrays bewaard en de hele arrays worden tussen functies doorgegeven om de voortgang van het spel bij te houden.
  • Gebruik van grafische afbeeldingen op VB 6: VB 6 biedt geen geweldige grafische mogelijkheden, maar de game is een goede introductie tot wat er beschikbaar is. Veel van de rest van deze serie is een verkenning van hoe GDI +, de volgende generatie Microsoft-graphics, de VB 6-level graphics vervangt.
  • Wiskundige berekeningen gebruiken voor programmabesturing: het programma maakt gebruik van slimme modulo (Mod) en berekeningen met gehele getallen met behulp van de twee-game markerarrays om te bepalen wanneer een "winst" met drie elementen heeft plaatsgevonden.

De programmeerklasse in dit artikel is misschien net iets voorbij het beginniveau, maar het zou goed moeten zijn voor "tussentijdse" programmeurs. Maar laten we beginnen op een elementair niveau om enkele van de concepten te illustreren en u op weg te helpen met uw carrière in Visual Basic-game programmeren. Zelfs studenten die meer gevorderd zijn, vinden het misschien een beetje een uitdaging om de objecten in de juiste vorm te krijgen.

Instructies Boter, kaas en eieren

Als je nog nooit Tic Tac Toe hebt gespeeld, zijn hier de regels. Twee spelers wisselen elkaar af door X's en Os in een 3x3 speelveld te plaatsen.

Voordat het spel begint, moeten beide spelers overeenkomen wie als eerste gaat en wie zijn bewegingen met welk symbool markeert. Na de eerste zet plaatsen de spelers beurtelings hun markeringen in een lege cel. Het doel van het spel is om de eerste speler te zijn met drie tekens in een horizontale, diagonale of verticale lijn. Als er geen lege cellen zijn en geen van beide spelers een winnende combinatie heeft, is het spel gelijkspel.

Programma starten

Voordat u met daadwerkelijke codering begint, is het altijd een goed idee om de namen van componenten die u gebruikt te wijzigen. Zodra u begint met coderen, wordt de naam automatisch gebruikt door Visual Basic, dus u wilt dat dit de juiste naam is. We gebruiken de formuliernaam frmTicTacToe en we zullen ook het bijschrift veranderen in "Over Boter, kaas en eieren".

Nadat het formulier is ingesteld, gebruikt u het besturingselement lijntoolbox om een ​​raster van 3 x 3 te tekenen. Klik op het lijngereedschap en teken vervolgens een lijn waar u het wilt. Je moet op deze manier vier lijnen maken en hun lengte en positie aanpassen om ze er goed uit te laten zien. Visual Basic heeft ook enkele handige hulpmiddelen in het menu Opmaak die u zullen helpen. Dit is een geweldige kans om met ze te oefenen.

Naast het speelraster hebben we enkele objecten nodig voor de X- en O-symbolen die op het rooster worden geplaatst. Aangezien er negen spaties in het raster staan, maken we een objectmatrix met negen spaties, elementen genoemd in Visual Basic.

Er zijn verschillende manieren om zo ongeveer alles in de Visual Basic-ontwikkelomgeving te doen, en het maken van besturingsmatrices is geen uitzondering. Waarschijnlijk is de eenvoudigste manier om het eerste label te maken (klikken en tekenen net als het lijngereedschap), het een naam te geven, alle attributen in te stellen (zoals Lettertype en ForeColor) en er vervolgens kopieën van te maken. VB 6 vraagt ​​of u een besturingsmatrix wilt maken. Gebruik de naam lblPlayGround voor het eerste label.

Als u de andere acht elementen van het raster wilt maken, selecteert u het eerste labelobject, stelt u de eigenschap Index in op nul en drukt u op CTRL + C (kopiëren). Nu kunt u op CTRL + V (plakken) drukken om een ​​ander labelobject te maken. Wanneer u objecten zoals deze kopieert, neemt elke kopie alle eigenschappen over behalve Index van de eerste. Index wordt voor elk exemplaar met één verhoogd. Dit is een besturingsmatrix omdat ze allemaal dezelfde naam hebben, maar verschillende indexwaarden.

Als u de array op deze manier maakt, worden alle kopieën op elkaar gestapeld in de linkerbovenhoek van het formulier. Sleep elk label naar een van de rasterposities. Zorg ervoor dat indexwaarden opeenvolgend in het raster staan. De logica van het programma hangt ervan af. Het labelobject met indexwaarde 0 moet zich in de linkerbovenhoek bevinden en het label rechtsonder moet index 8 hebben. Als de labels het speelraster bedekken, selecteert u elk label, klikt u met de rechtermuisknop en selecteert u Verzenden naar terug.

Omdat er acht mogelijke manieren zijn om het spel te winnen, hebben we acht verschillende lijnen nodig om de winst op het speelrooster te tonen. U gebruikt dezelfde techniek om een ​​nieuwe besturingsmatrix te maken. Teken eerst de lijn, noem deze linWin en stel de eigenschap Index in op nul. Gebruik vervolgens copy-paste techniek om nog zeven regels te produceren. De volgende afbeelding laat zien hoe u de indexnummers correct instelt.

Naast het label en de lijnobjecten heb je enkele opdrachtknoppen nodig om het spel te spelen en meer labels om de score bij te houden. De stappen om deze te maken zijn hier niet gedetailleerd, maar dit zijn de objecten die u nodig hebt.

Twee knopobjecten:

  • cmdNewGame
  • cmdResetScore

Frame-object fraPlayFirst met twee optieknoppen:

  • optXPlayer
  • optOPlayer

Frame-object fraScoreBoard met zes labels. Alleen lblXScore en lblOScore zijn gewijzigd in de programmacode.

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

Ten slotte heb je ook het labelobject lblStartMsg nodig om de knop cmdNewGame te 'maskeren' wanneer er niet op moet worden geklikt. Dit is niet zichtbaar in de onderstaande afbeelding omdat deze dezelfde ruimte inneemt als de opdrachtknop. Mogelijk moet u de opdrachtknop tijdelijk verplaatsen om dit label op het formulier te tekenen.

Tot nu toe is er nog geen VB-codering gedaan, maar we zijn er eindelijk klaar voor.

initialisatie

Nu kunt u eindelijk beginnen met het coderen van het programma. Als u dit nog niet hebt gedaan, wilt u misschien de broncode downloaden om mee te volgen, omdat de werking van het programma wordt uitgelegd.

Een van de eerste ontwerpbeslissingen die u moet nemen, is hoe u de huidige 'staat' van het spel kunt bijhouden. Met andere woorden, wat zijn de huidige X'en en Os's op het speelraster en wie vervolgens beweegt. Het concept van 'staat' is van cruciaal belang bij veel programmering, en met name belangrijk bij het programmeren van ASP en ASP.NET voor het web

Dit kan op verschillende manieren, dus het is een cruciale stap in de analyse. Als u dit probleem zelf oplost, wilt u misschien een stroomdiagram tekenen en verschillende opties met 'kladpapier' uitproberen voordat u begint met coderen.

Variabelen

Onze oplossing maakt gebruik van twee "tweedimensionale arrays" omdat dat helpt om de 'status' bij te houden door eenvoudig de array-indexen in programmalussen te wijzigen. De status van de linkerbovenhoek bevindt zich in het array-element met index (1, 1), de rechterbovenhoek bevindt zich in (1, 3), de rechteronderhoek in (3,3), enzovoort . De twee arrays die dit doen zijn:

iXPos (x, y)

en

iOPos (x, y)

Er zijn veel verschillende manieren waarop dit kan worden gedaan en de uiteindelijke VB.NET-oplossing in deze serie laat u zien hoe u dit kunt doen met slechts één eendimensionale array.

De programmering om deze arrays te vertalen in beslissingen voor het winnen van spelers en zichtbare displays in het formulier staan ​​op de volgende pagina.

Je hebt ook een paar globale variabelen nodig als volgt. Merk op dat deze in de algemene en aangiftecode voor het formulier staan. Dit maakt hen variabelen op "moduleniveau" waarnaar overal in de code voor dit formulier kan worden verwezen. Lees Meer informatie over het bereik van variabelen in Visual Basic Help.

Er zijn twee gebieden waar variabelen in ons programma worden geïnitialiseerd. Eerst worden enkele variabelen geïnitialiseerd terwijl het formulier frmTicTacToe wordt geladen.

Private Sub Form_Load ()

Ten tweede worden vóór elke nieuwe game alle variabelen die moeten worden gereset naar startwaarden toegewezen in een initialisatiesubroutine.

Sub InitPlayGround ()

Merk op dat de initialisatie van de formulierbelasting ook de initialisatie van de speelplaats aanroept.

Een van de kritieke vaardigheden van een programmeur is de mogelijkheid om de foutopsporingsfaciliteiten te gebruiken om te begrijpen wat de code doet. U kunt dit programma gebruiken om te proberen:

  • De code doorlopen met de F8-toets
  • Een horloge instellen op belangrijke variabelen, zoals sPlaySign of iMove
    Een breekpunt instellen en de waarde van variabelen opvragen. In de binnenste lus van de initialisatie:
lblPlayGround ((i - 1) * 3 + j - 1) .Caption = ""

Merk op dat dit programma duidelijk laat zien waarom het een goede programmeerpraktijk is om gegevens zoveel mogelijk in arrays te bewaren. Als u geen arrays in dit programma had, zou u code zoiets als dit moeten schrijven:

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

in plaats van dit:

Voor i = 0 tot 7
linWin (i) .Visible = False
Volgende ik

Een zet doen

Als een deel van het systeem kan worden beschouwd als 'het hart', is het subroutine lblPlayGround_Click. Deze subroutine wordt elke keer aangeroepen wanneer een speler op het speelraster klikt. (Klikken moeten zich binnen een van de negen lblPlayGround-elementen bevinden.) Merk op dat deze subroutine een argument heeft: (Indexeren als geheel getal). De meeste andere 'event-subroutines', zoals cmdNewGame_Click (), doen dat niet. Index geeft aan op welk labelobject is geklikt. Index bevat bijvoorbeeld de waarde nul voor de linkerbovenhoek van het raster en de waarde acht voor de rechteronderhoek.

Nadat een speler op een vierkant in het spelraster klikt, wordt de opdrachtknop om een ​​ander spel te starten, cmdNewGame, 'ingeschakeld' door het zichtbaar te maken. De status van deze opdrachtknop heeft een dubbele taak omdat deze later ook wordt gebruikt als een Booleaanse beslissingsvariabele Het gebruik van een eigenschapswaarde als beslissingsvariabele wordt meestal afgeraden omdat als het ooit nodig wordt om het programma te wijzigen (bijvoorbeeld om de opdrachtknop cmdNewGame altijd zichtbaar te maken), het programma onverwacht mislukt omdat je herinnert je misschien niet dat het ook wordt gebruikt als onderdeel van de programmalogica. Om deze reden is het altijd een goed idee om door programmacode te zoeken en het gebruik van alles wat je verandert te controleren bij het uitvoeren van programma-onderhoud, zelfs eigendomswaarden. Dit programma is in strijd met de deels om dit punt te maken en deels omdat dit een relatief eenvoudig stuk code is waar het gemakkelijker is om te zien wat er wordt gedaan en om problemen later te voorkomen.

Een spelerselectie van een spelveld wordt verwerkt door de GamePlay-subroutine aan te roepen met Index als argument.

Verplaatsing verwerken

Eerst controleer je of er op een leeg vierkant is geklikt.

Als lblPlayGround (xo_Move) .Caption = "" Dan

Als we er zeker van zijn dat dit een legitieme zet is, wordt de zet-teller (iMove) verhoogd. De volgende twee regels zijn erg interessant omdat ze de coördinaten vertalen van de eendimensionale If lblPlayGround-componentarray naar tweedimensionale indexen die u in iXPos of iOPos kunt gebruiken. Mod en gehele getallenverdeling (de 'backslash') zijn wiskundige bewerkingen die u niet elke dag gebruikt, maar hier is een geweldig voorbeeld dat laat zien hoe ze erg handig kunnen zijn.

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

De waarde xo_Move 0 wordt vertaald naar (1, 1), 1 tot (1, 2) ... 3 tot (2, 1) ... 8 tot (3, 3).

De waarde in sPlaySign, een variabele met modulebereik, houdt bij welke speler de zet heeft gedaan. Nadat de verplaatsingsarrays zijn bijgewerkt, kunnen de labelcomponenten in het speelraster worden bijgewerkt met het juiste teken.

If sPlaySign = "O" Dan
iOPos (x, y) = 1
iWin = CheckWin (iOPos ())
Anders
iXPos (x, y) = 1
iWin = CheckWin (iXPos ())
Stop als
lblPlayGround (xo_Move) .Caption = sPlaySign

Wanneer de X-speler bijvoorbeeld in de linkerbovenhoek van het raster klikt, hebben variabelen de volgende waarden:

Het gebruikersscherm toont alleen een X in het vak linksboven, terwijl de iXPos een 1 in het vak linksboven heeft en 0 in alle andere. De iOPos heeft 0 in elke doos.

De waarden veranderen wanneer de O-speler op het middelste vierkant van het raster klikt. Nu toont iOPos een 1 in het middelste vak terwijl het gebruikersscherm links een X toont en een O in het middelste vak. De iXPos toont alleen de 1 in de linkerbovenhoek, met 0 in alle andere vakken.

Nu je weet waar een speler op heeft geklikt en welke speler heeft geklikt (met behulp van de waarde in sPlaySign), hoef je alleen maar uit te zoeken of iemand een spel heeft gewonnen en erachter te komen hoe je dat op het display kunt weergeven.

Een winnaar vinden

Na elke zet controleert de CheckWin-functie de winnende combinatie. CheckWin werkt door elke rij, elke kolom en elke diagonaal op te tellen. Het volgen van de stappen door CheckWin met behulp van de foutopsporingsfunctie van Visual Basic kan erg leerzaam zijn. Een overwinning vinden is een kwestie van eerst controleren of er drie 1's zijn gevonden in elk van de afzonderlijke cheques in de variabele iScore en vervolgens een unieke "handtekening" -waarde in Checkwin retourneren die wordt gebruikt als de array-index om de eigenschap Visible van te wijzigen. één element in de linWin-componentarray. Als er geen winnaar is, bevat CheckWin de waarde -1. Als er een winnaar is, wordt de weergave bijgewerkt, wordt het scorebord gewijzigd, wordt een felicitatiebericht weergegeven en wordt het spel opnieuw gestart.

Laten we een van de controles in detail bekijken om te zien hoe het werkt. De anderen zijn vergelijkbaar.

'Controleer rijen voor 3
Voor i = 1 tot 3
iScore = 0
CheckWin = CheckWin + 1
Voor j = 1 tot 3
iScore = iScore + iPo's (i, j)
Volgende j
Als iScore = 3 dan
Functie afsluiten
Stop als
Volgende ik

Het eerste dat opvalt, is dat de eerste indexteller i de rijen aftelt, terwijl de tweede j over de kolommen telt. De buitenste lus gaat dan eenvoudig van de ene rij naar de volgende. De binnenste lus telt de 1's in de huidige rij. Als er drie zijn, heb je een winnaar.

Merk op dat u ook het totale aantal geteste vierkanten bijhoudt in de variabele CheckWin, wat de waarde is die wordt doorgegeven wanneer deze functie wordt beëindigd. Elke winnende combinatie heeft een unieke waarde in CheckWin van 0 tot 7 die wordt gebruikt om een ​​van de elementen in de linWin () -componentmatrix te selecteren. Dit maakt ook de volgorde van de code in functie CheckWin belangrijk! Als u een van de blokken met luscode verplaatst (zoals hierboven), wordt de verkeerde lijn getekend op het speelraster wanneer iemand wint. Probeer het en zie!

Afwerkingsdetails

De enige code die nog niet is besproken, is de subroutine voor een nieuw spel en de subroutine die de score zal resetten. De rest van de logica in het systeem maakt het vrij eenvoudig om deze te maken. Om een ​​nieuw spel te starten, hoef je alleen de InitPlayGround-subroutine aan te roepen. Voor het gemak van spelers, omdat op de knop kan worden geklikt in het midden van een spel, vraagt ​​u om bevestiging voordat u doorgaat. U vraagt ​​ook om bevestiging voordat u het scorebord opnieuw start.