Tre på rad mot datamaskinen
Skrevet av: Oversatt fra Code Club UK
Oversatt av: Geir Arne Hjelle
Introduksjon
I dag skal vi prøve å skrive kode slik at datamaskinen kan spille tre på rad mot oss. Datamaskinen vil ikke spille så bra i begynnelsen, men etterhvert som den lærer noen triks vil den kanskje klare å vinne mot deg!
Steg 1: Vi fortsetter fra forrige gang
I leksjon 6 skrev vi et tre-på-rad spill for to spillere. Vi brukte Tk lerretet fra tkinter
-biblioteket for å tegne på skjermen. La oss se på hva vi allerede har før vi begynner å skrive ny kode.
Sjekkliste
-
from tkinter import * main = Tk() c = Canvas(main, width=600, height=600) c.pack() c.create_line(200, 0, 200, 600) c.create_line(400, 0, 400, 600) c.create_line(0, 200, 600, 200) c.create_line(0, 400, 600, 400) grid = [ "0", "1", "2", "3", "4", "5", "6", "7", "8", ] def click(event): shape = choose_shape() across = int(c.canvasx(event.x) / 200) down = int(c.canvasy(event.y) / 200) square = across + (down * 3) if grid[square] == "X" or grid[square] == "O": return if winner(): return if shape == "O": c.create_oval(across * 200, down * 200, (across+1) * 200, (down+1) * 200) grid[square] = "O" else: c.create_line(across * 200, down * 200, (across+1) * 200, (down+1) * 200) c.create_line(across * 200, (down+1) * 200, (across+1) * 200, down * 200) grid[square] = "X" def choose_shape(): if grid.count("O") > grid.count("X"): return "X" else: return "O" def winner(): for across in range(3): row = across * 3 line = grid[row] + grid[row+1] + grid[row+2] if line == "XXX" or line == "OOO": return True for down in range(3): line = grid[down] + grid[down+3] + grid[down+6] if line == "XXX" or line == "OOO": return True line = grid[0] + grid[4] + grid[8] if line == "XXX" or line == "OOO": return True line = grid[2] + grid[4] + grid[6] if line == "XXX" or line == "OOO": return True c.bind("<Button-1>", click) mainloop()
-
Du skal kunne klikke i rutene for å plassere sirkler og kryss inntil noen får tre på rad.
-
def click(event): shape = choose_shape() across = int(c.canvasx(event.x) / 200) down = int(c.canvasy(event.y) / 200) square = across + (down * 3) if grid[square] == "X" or grid[square] == "O": return if winner(): return grid[square] = shape draw_shape(shape, across, down) def draw_shape(shape, across, down): if shape == "O": c.create_oval(across * 200, down * 200, (across+1) * 200, (down+1) * 200) else: c.create_line(across * 200, down * 200, (across+1) * 200, (down+1) * 200) c.create_line(across * 200, (down+1) * 200, (across+1) * 200, down * 200)
Kjør koden og test at den fortsatt fungerer på samme måte som tidligere. Dette er et eksempel på noe som kalles refaktorering. Vi har endret på selve koden, men ikke endret hvordan programmet fungerer.
Steg 2: Spill tilfeldig
Før vi kan lære datamaskinen hvordan den gjør gode trekk vil vi lære den hvordan den gjør trekk i det hele tatt. Vi begynner med å la datamaskinen finne en tilfeldig ledig rute, og deretter spille der.
Husk at vi har en variabel som heter grid
som kan fortelle oss hvordan brettet ser ut. Det er en liste som starter som ["0", "1", "2", ... ]
, hvor vi putter inn "X"
og "O"
etterhvert som vi spiller. Vi begynner med å finne ledige ruter i denne listen for deretter å spille en slik rute.
Sjekkliste
-
def free_squares(): output = [] for position, square in enumerate(grid): if square != "X" and square != "O": output.append(position) return output
Denne prosedyren lager en tom liste. Deretter går den gjennom hele rutenettet og sjekker hver rute om den er tom.
Kommandoen
enumerate
kan fortelle oss posisjonen til hvert element igrid
-listen. For eksempel vilenumerate
gjøre om en liste['A','B','C']
til parene(0, 'A'), (1,'B'), (2, 'C')
slik at vi ikke trenger å telle elementene selv. -
from tkinter import * import random
Du husker kanskje at vi brukte
random.choice
i en tidligere leksjon om Hangman. -
def play_move(): moves = free_squares() square = random.choice(moves) across = square % 3 down = square // 3 grid[square] = "X" draw_shape("X", across, down)
Først bruker vi
free_squares
til å lage en liste over de tomme rutene. Deretter velger vi en tilfeldig av disse rutene. Vi vil nå oversette dette rutenummeret til rad- og kolonne-nummer. Dette gjør vi ved å bruke%
og//
operatorene. La oss se litt nærmere på hvordan dette virker:0 1 2 ----- 0 | 0 1 2 1 | 3 4 5 2 | 6 7 8
For eksempel er rute nummer 5 i rad 1 og kolonne 2. Hvis vi deler 5 på 3 får vi 1 med 2 i rest.
5 // 3
er 1,6 // 3
er 2, og så videre. Operatoren//
forteller oss hvor mange ganger et tall deler et annet, men ser bort i fra resten. Siden vi har 3 kolonner forteller5 // 3
oss i hvilken rad rute 5 er.5 % 3
er 2,6 % 3
er 0. Operatoren%
forteller oss hva resten er når vi deler et tall med et annet. Dette gir oss kolonnenummeret.Legg merke til at de to linjene
across = square % 3 down = square // 3
gjør den motsatte utregningen av
square = across + (down * 3)
som vi allerede har brukt i
click
. -
def click(event): across = int(c.canvasx(event.x) / 200) down = int(c.canvasy(event.y) / 200) square = across + (down * 3) if grid[square] == "X" or grid[square] == "O": return if winner(): return grid[square] = "O" draw_shape("O", across, down) if winner(): return play_move()
Vi sjekker først om spilleren har vunnet, og hvis ikke lar vi datamaskinen gjøre sitt trekk.
Steg 3: Velg et trekk som vinner
Datamaskinen spiller nå tre på rad, men den er ikke spesielt flink. La oss hjelpe den litt. I stedet for å bare velge trekk helt tilfeldig, la datamaskinen velge trekk som gjør at den vinner om de finnes. Ideen er at vi kan sjekke alle de mulige trekkene til datamaskinen, og om ett av disse vil vinne spillet lar vi datamaskinen spille det.
Sjekkliste
-
def winner(grid): for across in range(3): row = across * 3 line = grid[row] + grid[row+1] + grid[row+2] if line == "XXX" or line == "OOO": return True for down in range(3): line = grid[down] + grid[down+3] + grid[down+6] if line == "XXX" or line == "OOO": return True line = grid[0] + grid[4] + grid[8] if line == "XXX" or line == "OOO": return True line = grid[2] + grid[4] + grid[6] if line == "XXX" or line == "OOO": return True
Du trenger bare å endre den første linjen i prosedyren. Dette betyr at
winner
vil bruke en liste vi sender til den, i stedet forgrid
som husker hvordan dette spillet ser ut. Dermed kanwinner
også undersøke trekk som ikke er blitt spilt enda. -
def click(event): across = int(c.canvasx(event.x) / 200) down = int(c.canvasy(event.y) / 200) square = across + (down * 3) if grid[square] == "X" or grid[square] == "O": return if winner(grid): return grid[square] = "O" draw_shape("O", across, down) if winner(grid): return play_move()
Alle steder vi har
winner()
i koden bytter vi det ut medwinner(grid)
. -
def play_move(): moves = free_squares() square = random.choice(moves) # Bruk et vinnende trekk om det eksisterer for possible in moves: new_grid = list(grid) new_grid[possible] = "X" if winner(new_grid): square = possible break across = square % 3 down = square // 3 grid[square] = "X" draw_shape("X", across, down)
For hver ledige rute lager vi en kopi av
grid
-listen med kommandoenlist(grid)
. Deretter plasserer vi en X i denne ledige ruten og brukerwinner
for å undersøke om dette vil være et vinnende trekk!
Steg 4: Velg et trekk som blokkerer
Den andre strategien vi vil lære datamaskinen er å blokkere trekk som gjør at vi vil vinne. Dette gjør vi på nesten samme måte, men nå ser vi hva som skjer om vi plasserer ut O i de ledige rutene.
Sjekkliste
-
def play_move(): moves = free_squares() square = random.choice(moves) # Bruk et blokkerende trekk om det eksisterer for possible in moves: new_grid = list(grid) new_grid[possible] = "O" if winner(new_grid): square = possible break # Bruk et vinnende trekk om det eksisterer for possible in moves: new_grid = list(grid) new_grid[possible] = "X" if winner(new_grid): square = possible break across = square % 3 down = square // 3 grid[square] = "X" draw_shape("X", across, down)
Legg merke til at datamaskinen først plukker en tilfeldig ledig rute. Deretter sjekker den om den kan blokkere, og hvis den kan det så ombestemmer den seg. Til slutt sjekker den om den kan vinne, og dersom den kan det så ombestemmer den seg en gang til!
Hele programmet
Det ferdige programmet ditt vil nå se omtrent ut som dette!
from tkinter import *
import random
main = Tk()
c = Canvas(main, width=600, height=600)
c.pack()
c.create_line(200, 0, 200, 600)
c.create_line(400, 0, 400, 600)
c.create_line(0, 200, 600, 200)
c.create_line(0, 400, 600, 400)
grid = [
"0", "1", "2",
"3", "4", "5",
"6", "7", "8",
]
def click(event):
across = int(c.canvasx(event.x) / 200)
down = int(c.canvasy(event.y) / 200)
square = across + (down * 3)
if grid[square] == "X" or grid[square] == "O":
return
if winner(grid):
return
grid[square] = "O"
draw_shape("O", across, down)
if winner(grid):
return
play_move()
def draw_shape(shape, across, down):
if shape == "O":
c.create_oval(across * 200, down * 200,
(across+1) * 200, (down+1) * 200)
else:
c.create_line(across * 200, down * 200,
(across+1) * 200, (down+1) * 200)
c.create_line(across * 200, (down+1) * 200,
(across+1) * 200, down * 200)
def winner(grid):
for across in range(3):
row = across * 3
line = grid[row] + grid[row+1] + grid[row+2]
if line == "XXX" or line == "OOO":
return True
for down in range(3):
line = grid[down] + grid[down+3] + grid[down+6]
if line == "XXX" or line == "OOO":
return True
line = grid[0] + grid[4] + grid[8]
if line == "XXX" or line == "OOO":
return True
line = grid[2] + grid[4] + grid[6]
if line == "XXX" or line == "OOO":
return True
def free_squares():
output = []
for position, square in enumerate(grid):
if square != "X" and square != "O":
output.append(position)
return output
def play_move():
moves = free_squares()
square = random.choice(moves)
# Bruk et blokkerende trekk om det eksisterer
for possible in moves:
new_grid = list(grid)
new_grid[possible] = "O"
if winner(new_grid):
square = possible
break
# Bruk et vinnende trekk om det eksisterer
for possible in moves:
new_grid = list(grid)
new_grid[possible] = "X"
if winner(new_grid):
square = possible
break
down = square // 3
across = square % 3
grid[square] = "X"
draw_shape("X", across, down)
c.bind("<Button-1>", click)
mainloop()
Utfordring
Det er fortsatt mulig å vinne mot datamaskinen. Kan du gjøre endringer som gjør at den spiller enda bedre? Kanskje du kan lære datamaskinen å spille perfekt?
Lisens: Code Club World Limited Terms of Service
Forbedre denne siden
Funnet en feil? Kunne noe vært bedre?
Hvis ja, vennligst gi oss tilbakemelding ved å lage en sak på Github eller fiks feilen selv om du kan. Vi er takknemlige for enhver tilbakemelding!