Python: Connect 4 with TKinter - python

Alrighty I'm trying to implement a GUI for a game of Connect Four by using TKinter. Now I have the grid and everything set up what I'm having trouble with is getting the chip to show up on the board.
Here is my output:
What I'm trying to do is make it so when I click one of the bottom column buttons a chip appears (and since this is connect four it should go from bottom to top)
Here is my code:
from Tkinter import *
from connectfour import *
from minimax import *
from player import *
import tkMessageBox
class ConnectFourGUI:
def DrawGrid(self):
for i in range(0,self.cols+1):
self.c.create_line((i+1)*self.mag,self.mag,\
(i+1)*self.mag,(self.rows+1)*self.mag)
for i in range(0,self.rows+1):
self.c.create_line(self.mag,(i+1)*self.mag,\
self.mag*(1+self.cols),(i+1)*self.mag)
def __init__(self,wdw):
wdw.title("Connect Four")
self.mag = 60
self.rows = 6
self.cols = 7
self.c = Canvas(wdw,\
width=self.mag*self.cols+2*self.mag,\
height = self.mag*self.rows+2*self.mag,\
bg='white')
self.c.grid(row=1,column=1,columnspan=2)
rlabel=Label(root, text="Player1:")
rlabel.grid(row=0,column=0)
self.player1_type=StringVar(root)
options= ["Human", "Random", "Minimax"]
self.player1_type.set(options[2])
self.rowbox=OptionMenu(root, self.player1_type, *options)
self.rowbox.grid(row=0, column=1)
rlabel2=Label(root, text="Player2:")
rlabel2.grid(row=0,column=2)
self.player2_type=StringVar(root)
self.player2_type.set(options[0])
self.rowbox=OptionMenu(root, self.player2_type, *options)
self.rowbox.grid(row=0, column=3)
begin=Button(root, text="Start", command=self.game_start)
begin.grid(row=0, column=4)
self.c.grid(row=1, column=0, columnspan=7)
play_col=[]
for i in range(self.cols):
play_col.append(Button(root, text= "Col %d" %i, command=lambda col= i: self.human_play(col)))
play_col[i].grid(row=10,column="%d"%i)
## self.DrawCircle(1,1,1)
## self.DrawCircle(2,2,1)
## self.DrawCircle(5,3,2)
self.DrawGrid()
self.brd = ConnectFour()
def game_start(self):
self.board=ConnectFour()
print self.player1_type.get()
print self.player2_type.get()
if self.player1_type.get()=="Random":
self.player1 = RandomPlayer(playernum=1)
if self.player2_type.get()== "Random" or self.player2_type.get() == "Minimax":
tkMessageBox.showinfo("Bad Choice", "You Have to choose At least 1 Human Player")
else:
self.player
elif self.player1_type.get()=="Minimax":
self.player1=MinimaxPlayer(playernum=2, ply_depth=4, utility=SimpleUtility(5,1))
if self.player2_type.get()== "Random" or self.player2_type.get() == "Minimax":
tkMessageBox.showinfo("Bad Choice", "You Have to choose At least 1 Human Player")
elif self.player1_type.get()=="Human":
self.player1=Human(playernum=1)
if self.player2_type.get()=="Human":
self.player2=Human(playernum=2)
elif self.player2_type.get()=="Random":
self.player2=RandomPlayer(playernum=2)
elif self.player2_type.get()=="Minimax":
self.player2=MinimaxPlayer(playernum=2, ply_depth=4, utility=SimpleUitlity(5,1))
#self.currentplayer==1
#self.draw()
def human_play(self, col):
if self.player1_type.get()=="Human" and self.player2_type.get() =="Human":
while True:
self.DrawCircle(row,col,1)
if self.brd.is_game_over() is None:
self.DrawCircle(row,col,2)
if self.brd.is_game_over() is None:
pass
else:
print "Player 2 wins!"
break
else:
print "Player 1 wins!"
break
def DrawCircle(self,row,col,player_num):
if player_num == 1:
fill_color = 'red'
elif player_num == 2:
fill_color = 'black'
#(startx, starty, endx, endy)
self.c.create_oval(col*self.mag,row*self.mag,(col+1)*self.mag,(row+1)*self.mag,fill=fill_color)
root=Tk()
ConnectFourGUI(root)
root.mainloop()
I know I'm supposed to call the DrawCircle function in the Human Play function, I'm just unsure as to how I'm supposed to set it all up. any advice as to how I could go about this would be greatly appreciated!

Your code is depending on a few packages I don't have, so I can't be more specific, but the way I'd go about doing something like this is to track the X and Y coords of the chip works out the the row and column that it'd go into in the widget.
You'll need to create a location object, a tuple maybe, and then a way to translate the location object into a drawable location. Then it's just a matter of incrementing either X or Y and detecting if there's a chip below it.

Related

Reset Game designed with tkinter

I am a beginner in python and have designed a "Count the Colored Balls Game". I am now stuck at figuring out best way of restarting the game after one round is over.
Currently:
When you hit start game, it populates 25 random colored balls and then gives user 10 seconds to count red and green balls. It them shows the final answer as a messagebox. Once i dismiss the message box, the balls remain on screen and then when I hit start game, 25 more balls are piled on.
What I want:
How do I reset the game such that when I complete one round, and hit start game, it clears the existing game and restarts.
My Code
from tkinter import *
import random
import time
#from tkinter import ttk
from tkinter import messagebox
#creating a list of colors:
colors=["blue","red","yellow","green","red","pink","red","black","green","cyan"]
#creating global variables and inititializing:
global i
i=0
global redcount
redcount=0
global greencount
greencount=0
global canvas
def startgame():
global canvas
x=startclick()
if x==1:
time.sleep(5)
messagebox.showinfo("Answer"," number of red balls "+str(redcount)+
" and number of green balls is "+str(greencount))
#==============================================
def startclick():
global i
global canvas
global redcount
global greencount
for i in range(1,26):
m=random.randint(0,10)
if m == 1 or m ==4 or m==6:
redcount=redcount+1
if m == 3 or m==8:
greencount=greencount+1
try:
a=random.randint(50,250)
b=random.randint(50,350)
canvas.create_oval(a,b,a+50,b+50,outline="white",fill=colors[m],width=1)
canvas.update()
except:
print()
return(1)
#===============================================
#===============================================
root=Tk()
root.title("Count The Colors")
root.geometry("800x800+0+0") #dimension and position of main frame
#creating canvas of the game
canvas = Canvas(width=600,height=500, bg = "#d2b48c")
canvas.place(x=20, y=20)
w=Label(root,text="Can you count the number of red and green balls?",bg="black",fg="yellow")
w.place(x=30,y=500)
y=Label(root, text="You have 10 seconds to answer...press start to play",
bg="white", fg="black")
y.place(x=20,y=550)
b=Button(root,text = "Start", bg="#fd5f00", width=20, height=1,
font=("Times",12,"bold"), fg="brown", command = startgame)
b.place(x=20, y=600)
root.mainloop()
Appreciate some help on this
First you are not following up programming conventions but anyways you can clear canvas everytime your startclick() function is being called. Simply add canvas.delete("all") to delete the previously created ovals.
def startclick():
global i
global canvas
global redcount
global greencount
canvas.delete("all") #This is your solution
for i in range(1, 26):
m = random.randint(0, 10)
if m == 1 or m == 4 or m == 6:
redcount = redcount + 1
if m == 3 or m == 8:
greencount = greencount + 1
try:
a = random.randint(50, 250)
b = random.randint(50, 350)
canvas.create_oval(a, b, a + 50, b + 50, outline="white", fill=colors[m], width=1)
canvas.update()
except:
print()
return (1)

Organising code for Tkinter Two-Player Game

I'm trying to make a two-player game where people take it in turns to make a move. I feel like I have to manually change the master window in order for the two to have the same content and widgets. How do I make things simpler for myself? How do I make switching between the two windows easier?
from tkinter import *
root = Tk()
root.title('Game')
def win1():
global board
top2.withdraw()
top1.deiconify()
board=top1
def win2():
top1.withdraw()
top2.deiconify()
board=top2
top1 = Toplevel()
board = top1
top1.title('Player 1')
top1.withdraw()
buttonp1 = Button(top1, text="Switch to Player 2", command=win2)
buttonp1.grid(row=15, column=0, columnspan=10)
top2 = Toplevel()
board = top2
top2.title('Player 2')
top2.withdraw()
buttonp2 = Button(top2,text="Switch to Player 1", command=win1)
buttonp2.grid(row=15, column=0, columnspan=10)
choice = Button(text="Submit Weapons Choices", command=win1)
choice.grid()
#After this button is pressed, win1 is opened, and the switching from window to window begins.
top_array = [top1, top2]
# When generating the board, I would use "for top in top_array", then run a function which generates widgets. I want the board to start off with the exact same layout.
Problems that I run into involve not being able to change text variables later on in the code, because it was generated in a function:
top_array = [top1, top2]
def init(board):
txt = StringVar(board)
txt.set("\nClick anywhere to begin\n")
label = Label(board, textvariable=txt)
label.grid(row = 11, column = 0, columnspan=10)
for top in top_array:
init(top)
I used a class to make both windows start off with the same content, but have different titles, etc. and an array so that I could switch between the two
class Player:
def __init__(self, board, tag, name):
self.board = board
self.tag = tag
self.name = name
p = Player(root,"","")
board1 = Toplevel()
board2 = Toplevel()
gamemode = [Player(board1, "Switch to Player 2", "Player 1"), Player(board2, "Switch to Player 1", "Player 2")]
def switch():
global pnum
pnum +=1
pnum %= 2
gamemode[pnum].board.withdraw()
gamemode[((pnum+1)%2)].board.deiconify()

How to see which button is activated / determine right buttons are activated on python tkinter

I'm making a game that finding seven right buttons(It has a fixed answer) among 12 buttons by using tkinter. There are a total of 12 buttons, and only if all seven of them are clicked will win the game.
I'm curious about how to make the window to see which button is activated or inactivated, and how to make a function to determine whether one wins or loses. (when seven specific buttons are activated, it wins.)
++
game1=tkinter.Tk()
game1.title("Test")
game1.geometry("600x450")
button1 = Button(game1, text=' 1 ', fg='black', bg='red',
height=3, width=10)
button1.place(x=0,y=300)
button2 = Button(game1, text=' 2 ', fg='black', bg='red',
height=3, width=10)
button2.place(x=100,y=300)
game1.mainloop()
This is the first time using tkinter on python so actually I stopped after writing this really basic code.
The player can choose seven buttons until player itself clicks the "finish" button. (after click that button, player cannot modify anything.)
At first, I thought if I declare "num" and +=1 on that when the right buttons are clicked, but this trial failed because the player can choose whether one activates or inactivates until until player itself clicks the "finish" button. So I thought that this code needs the way to check the final statements of the buttons.
Is there any ways to use something like "if" statements on tkinter? (if players choose right seven buttons, so it is found that they're activated --> then player wins.)
EDIT - I've updated the code:
import tkinter as tk
import tkinter.ttk as ttk
class Application(tk.Tk):
def __init__(self, *args, **kwargs):
from random import sample
tk.Tk.__init__(self, *args, **kwargs)
self.title("Buttons")
self.resizable(width=False, height=False)
row_count = 3
column_count = 4
self.winning_buttons_count = 7
assert 0 < self.winning_buttons_count <= row_count * column_count
winning_buttons_numbers = sample(range(0, row_count * column_count), k=self.winning_buttons_count)
self.selected_buttons_count = 0
self.selected_button_color = "gray"
self.correct_button_color = "green"
self.incorrect_button_color = "red"
self.missed_button_color = "orange"
self.default_button_color = tk.Button().cget("bg")
def on_press(button):
color = button.cget("bg")
if color == self.default_button_color and self.selected_buttons_count < self.winning_buttons_count:
button.configure(bg=self.selected_button_color)
button.configure(relief=tk.SUNKEN)
self.selected_buttons_count += 1
elif color == self.selected_button_color:
button.configure(bg=self.default_button_color)
button.configure(relief=tk.RAISED)
self.selected_buttons_count -= 1
def check_win():
selected_winning_count = 0
for button in self.buttons:
is_selected = button.cget("bg") == self.selected_button_color and \
button.cget("relief") == tk.SUNKEN
is_winning = button.number in winning_buttons_numbers
if is_selected:
if is_winning:
button.configure(bg=self.correct_button_color)
selected_winning_count += 1
else:
button.configure(bg=self.incorrect_button_color)
else:
if is_winning:
button.configure(bg=self.missed_button_color)
if selected_winning_count == self.winning_buttons_count:
self.finish_button.configure(text="You won!")
else:
self.finish_button.configure(text="Incorrect.")
self.buttons = []
for row in range(row_count):
for column in range(column_count):
button = tk.Button(self, text=" " * 8)
button.grid(row=row, column=column)
button.number = (row * column_count) + column
button.configure(command=lambda b=button: on_press(b))
self.buttons.append(button)
vertical_line = ttk.Separator(self, orient=tk.VERTICAL)
vertical_line.grid(row=0, column=column_count+1, rowspan=row_count, sticky="ns", padx=(8, 8))
self.finish_button = tk.Button(self, text="Did I win?", command=check_win)
self.finish_button.grid(row=row_count//2, column=column_count+2)
def main():
application = Application()
application.mainloop()
return 0
if __name__ == "__main__":
import sys
sys.exit(main())

Tkinter, button click with if Statement for a specific picture in a label

I'm trying to do a quiz with 4 pictures given randomly (only one at a time in a label). Below there are 4 buttons which are the answer options. The buttons always stay the same, just the pictures change randomly after answering the right question. So when there is a picture, after pressing the right button the picture should change into another one. If pressing the wrong button, nothing should happen.
So far, the problem is at referring to the picture which is seen at the moment in the right way.
Right now, I tried to change the picture into the next one after answering correctly, because I didn't know how to insert a random change.
Thanks for every help!
from tkinter import *
from random import randint
Fenster = Tk()
Fenster.title('training')
Fenster.geometry('1024x720')
# images
img110 = PhotoImage(file='1.gif')
img120 = PhotoImage(file='2.gif')
img130 = PhotoImage(file='3.gif')
img140 = PhotoImage(file='4.gif')
# Label image
bild=randint(1,4)
if bild==1:
labelbild = Label(image=img110)
elif bild==2:
labelbild = Label(image=img120)
elif bild==3:
labelbild = Label(image=img130)
elif bild==4:
labelbild = Label(image=img140)
labelbild.place(x=350, y=150)
#actions
def button110Click():
if bild==1:
labelbild.config(image=img120)
else:
pass
def button120Click():
if bild==2:
labelbild.config(image=img130)
else:
pass
def button130lick():
if bild==3:
labelbild.config(image=img140)
else:
pass
def button140Click():
if bild==4:
labelbild.config(image=img110)
else:
pass
# Buttons
button110 = Button(master=Fenster, text='108', bg='#D5E88F', command=button110Click)
button110.place(x=350, y=420, width=40, height=40)
button120 = Button(master=Fenster, text='120', bg='#FFCFC9', command=button120Click)
button120.place(x=440, y=420, width=40, height=40)
button130 = Button(master=Fenster, text='128.57', bg='#FBD975', command=button130Click)
button130.place(x=530, y=420, width=40, height=40)
button140 = Button(master=Fenster, text='135', bg='#FBD975', command=button140Click)
button140.place(x=620, y=420, width=40, height=40)
Fenster.mainloop()
I suggest using a list to hold the images, and use bild as the index into this list.
from random import randint
import tkinter
# number of images
N = 4
# Use a Python 'list comprehension' to build a list of images
images = [ PhotoImage(file='%d.gif') % i for i in range(1,N+1)]
bild = 0
def new_image():
# Select and display the an image
global bild
bild = randint(0,N-1)
labelbild = label(images[bild])
labelbild.place(x=350, y=150)
new_image()
# actions
# (I bet there's a parametric way to do this using one function, but I don't know tkinter)
def button110Click():
if bild == 0:
new_image()
def button120Click():
if bild == 1:
new_image()
def button130Click():
if bild == 2:
new_image()
def button140Click():
if bild == 3:
new_image()
# The rest is the same as in the OP.
Since I don't have tkinter I was unable to test the complete application, so there may be bugs.

Python tkinter Entry widget usage

I'm currently trying to use the entry widget in tkinter to get a number from the user, and then use that number to define a parameter of one of my other functions.
The code is very long, so I'll try summarize my thoughts after the block.
class NimGame():
def __init__(self, numberOfBalls):
self.numberOfBallsRemaining = numberOfBalls
print("Nim game initialized with {} balls.".format(self.numberOfBallsRemaining))
def remainingBalls(self):
return self.numberOfBallsRemaining
def take(self, numberOfBalls):
if (numberOfBalls < 1) or (numberOfBalls > 3) or (numberOfBalls > self.numberOfBallsRemaining):
print("You can't take that number of balls. Try again.")
# ## Update Label in the GUI to tell user they can't take that many balls.
# It might be better to "inactivate" the buttons that correspond to invalid number to take.
statusLabel.configure(text="You can't take that number of balls. Try again.")
else:
self.numberOfBallsRemaining = self.numberOfBallsRemaining - numberOfBalls
print("You took {} balls. {} remain.".format(numberOfBalls, self.numberOfBallsRemaining))
if self.numberOfBallsRemaining == 0:
print("Computer wins!")
else:
# ## After removing player-chosen balls, update graphics and then pause for 1.0 seconds
# before removing computer-chosen balls. This way the player can "see" the
# intermediate status. Perhaps think about a nicer way of showing this.
updateGraphics()
sleep(1.0)
computerMaxBalls = min(3, self.numberOfBallsRemaining)
compBallsTaken = random.randint(1,computerMaxBalls)
self.numberOfBallsRemaining = self.numberOfBallsRemaining - compBallsTaken
# ## After removing computer-chosen balls, update graphics again.
updateGraphics()
print("Computer took {} balls. {} remain.".format(compBallsTaken, self.numberOfBallsRemaining))
if self.numberOfBallsRemaining == 0:
print("You win!")
def updateGraphics():
canvas.delete('all')
centerX = leftmostBallXPosition
centerY = ballYPosition
for i in range(nimGame.remainingBalls()):
canvas.create_oval(centerX - halfBallSize,
centerY - halfBallSize,
centerX + halfBallSize,
centerY + halfBallSize,
fill="#9999ff")
centerX = centerX + spaceBetweenBalls + ballSize
canvas.update_idletasks()
def initializeNewGame():
numberOfBalls = e1.get()
initializeNimAndGUI(numberOfBalls)
def initializeNimAndGUI(numberOfBalls):
global nimGame
global ballSize, halfBallSize, spaceBetweenBalls, leftmostBallXPosition, ballYPosition
nimGame = NimGame(numberOfBalls)
canvas.delete('all')
ballSize = min(maxBallSize, int(((canvasWidth-canvasBorderBuffer)//numberOfBalls)/1.2))
halfBallSize = ballSize // 2
spaceBetweenBalls = int(0.2 * ballSize)
leftmostBallXPosition = (canvasBorderBuffer//2) + (spaceBetweenBalls//2) + halfBallSize
ballYPosition = canvasHeight // 2
updateGraphics()
def createGUI():
global rootWindow
global canvas
global statusLabel
global textEntry
global e1
rootWindow = Tk()
canvasAndButtons = Frame(rootWindow)
canvas = Canvas(canvasAndButtons, height=canvasHeight, width=canvasWidth, relief=SUNKEN, borderwidth=2)
canvas.pack(side=LEFT)
buttonframe = Frame(canvasAndButtons)
e1 = Entry(buttonframe)
e1.pack()
button1 = Button(buttonframe, text='Take 1', command=lambda:takeBalls(1))
button2 = Button(buttonframe, text='Take 2', command=lambda:takeBalls(2))
button3 = Button(buttonframe, text='Take 3', command=lambda:takeBalls(3))
button4 = Button(buttonframe, text='New Game', command=initializeNewGame)
button1.pack()
button2.pack()
button3.pack()
button4.pack()
buttonframe.pack(side=RIGHT)
canvasAndButtons.pack()
statusLabel = Label(rootWindow, text="Play Nim")
statusLabel.pack()
def runNim(numberOfBalls):
createGUI()
initializeNimAndGUI(numberOfBalls)
rootWindow.mainloop()
So to me it seems that the program is having issues updating the graphics when I do a second game. What should happen is that, for example, i call runNim(20) and play a game with 20 balls. After the game, I need to be able to enter a number in the entry widget, click new game, and have that be the new numberOfBalls. When I do that, I get the init message back stating "Nim game initialized with 10 balls." But the GUI doesnt change, no balls appear in the GUI, and if I try to take anything it gives traceback and errors.
I figured out my problem. When I was trying to convert the string from e1.get() into an integer, I wasn't accounting for the empty string when I first run the program. This gave me a ValueError: invalid literal for int() with base 10: ''. So I had to account for that, and now it works.

Categories

Resources