Python Turtle Write Value in Containing Box - python

I want to be able to create some turtles which display values by subclassing turtle.Turtle.
These turtles should display their value as text centered in their own shape. I also want to be able to position the turtles with accuracy, so setting/determining their width and height relative to a given font size is important.
This is my attempt so far:
I think this answer is relevant: How to know the pixel size of a specific text on turtle graphics in python? but it is quite old, and the bounding box it draws around the text is not in the correct position using python 3.8.
import turtle
FONT_SIZE = 32
class Tile(turtle.Turtle):
def __init__(self):
super().__init__(shape="square")
self.penup()
def show_value(self, val):
self.write(val, font=("Arial", FONT_SIZE, "bold"), align="center")
screen = turtle.Screen()
vals = [5, 7, 8, 2]
for i in range(len(vals)):
tile = Tile()
tile_size = (FONT_SIZE / 20)
tile.shapesize(tile_size)
tile.fillcolor("red" if i % 2 == 0 else "blue")
tile.setx(i * FONT_SIZE)
tile.show_value(vals[i])
turtle.done()

It would be very helpful to have Turtle Objects containing text such
as integer values, which can be used to display a variety of puzzles
and games, and can have their own click handlers attached.
Here's the rub, and the (two) reason(s) that approaches using stamp() as suggested in other answers won't work. First, you can't click on a hidden turtle:
from turtle import *
def doit(x, y):
print("Just do it!")
yertle = Turtle()
# comment out the following line if you want `onlick()` to work
yertle.hideturtle()
yertle.shape('square')
yertle.stamp()
yertle.onclick(doit)
done()
Stamps are not clickable entities. Second, you can't even click on a turtle that's behind ink left by this, or another, turtle:
from turtle import *
def doit(x, y):
print("Just do it!")
yertle = Turtle()
yertle.shape('square')
yertle.fillcolor('white')
yertle.onclick(doit)
myrtle = Turtle()
myrtle.shape('turtle')
myrtle.penup()
myrtle.sety(-16)
# comment out the following line if you want `onlick()` to work
myrtle.write('X', align='center', font=('Courier', 32, 'bold'))
myrtle.goto(100, 100) # move myrtle out of the way of clicking
done()
If you click on the letter 'X', nothing happens unless you manage to hit a portion of the square just beyond the letter. My belief is that although we think of the 'X' as dead ink over our live turtle, at the tkinter level they are both similar, possibly both capable of receiving events, so one obscures the click on the other.
So how can we do this? The approach I'm going to use is make a tile a turtle with an image where the images are generate by writing onto bitmaps:
tileset.py
from turtle import Screen, Turtle, Shape
from PIL import Image, ImageDraw, ImageFont, ImageTk
DEFAULT_FONT_FILE = "/Library/Fonts/Courier New Bold.ttf" # adjust for your system
DEFAULT_POINT_SIZE = 32
DEFAULT_OUTLINE_SIZE = 1
DEFAULT_OUTLINE_COLOR = 'black'
DEFAULT_BACKGROUND_COLOR = 'white'
class Tile(Turtle):
def __init__(self, shape, size):
super().__init__(shape)
self.penup()
self.size = size
def tile_size(self):
return self.size
class TileSet():
def __init__(self, font_file=DEFAULT_FONT_FILE, point_size=DEFAULT_POINT_SIZE, background_color=DEFAULT_BACKGROUND_COLOR, outline_size=DEFAULT_OUTLINE_SIZE, outline_color=DEFAULT_OUTLINE_COLOR):
self.font = ImageFont.truetype(font_file, point_size)
self.image = Image.new("RGB", (point_size, point_size))
self.draw = ImageDraw.Draw(self.image)
self.background_color = background_color
self.outline_size = outline_size
self.outline_color = outline_color
def register_image(self, string):
width, height = self.draw.textsize(string, font=self.font)
image = Image.new("RGB", (width + self.outline_size*2, height + self.outline_size*2), self.background_color)
draw = ImageDraw.Draw(image)
tile_size = (width + self.outline_size, height + self.outline_size)
draw.rectangle([(0, 0), tile_size], outline=self.outline_color)
draw.text((0, 0), string, font=self.font, fill="#000000")
photo_image = ImageTk.PhotoImage(image)
shape = Shape("image", photo_image)
Screen()._shapes[string] = shape # underpinning, not published API
return tile_size
def make_tile(self, string):
tile_size = self.register_image(string)
return Tile(string, tile_size)
Other than its image, the only differences a Tile instance has from a Turtle instance is an extra method tile_size() to return its width and height as generic turtles can't do this in the case of images. And a tile's pen is up at the start, instead of down.
I've drawn on a couple of SO questions and answers:
Dump characters (glyphs) from TrueType font (TTF) into bitmaps
How do you set a turtle's shape to a PIL image
And while I'm at it, this answer has been updated to be more system independent:
How to know the pixel size of a specific text on turtle graphics in python?
To demonstrate how my tile sets work, here's the well-know 15 puzzle implemented using them. It creates two tile sets, one with white backgrounds and one with red (pink) backgrounds:
from tileset import TileSet
from turtle import Screen
from functools import partial
from random import shuffle
SIZE = 4
OFFSETS = [(-1, 0), (0, -1), (1, 0), (0, 1)]
def slide(tile, row, col, x, y):
tile.onclick(None) # disable handler inside handler
for dy, dx in OFFSETS:
try:
if row + dy >= 0 <= col + dx and matrix[row + dy][col + dx] == None:
matrix[row][col] = None
row, col = row + dy, col + dx
matrix[row][col] = tile
width, height = tile.tile_size()
x, y = tile.position()
tile.setposition(x + dx * width, y - dy * height)
break
except IndexError:
pass
tile.onclick(partial(slide, tile, row, col))
screen = Screen()
matrix = [[None for _ in range(SIZE)] for _ in range(SIZE)]
white_tiles = TileSet(background_color='white')
red_tiles = TileSet(background_color='pink')
tiles = []
parity = True
for number in range(1, SIZE * SIZE):
string = str(number).rjust(2)
tiles.append(white_tiles.make_tile(string) if parity else red_tiles.make_tile(string))
parity = not parity
if number % SIZE == 0:
parity = not parity
shuffle(tiles)
width, height = tiles[0].tile_size()
offset_width, offset_height = width * 1.5, height * 1.5
for row in range(SIZE):
for col in range(SIZE):
if row == SIZE - 1 == col:
break
tile = tiles.pop(0)
width, height = tile.tile_size()
tile.goto(col * width - offset_width, offset_height - row * height)
tile.onclick(partial(slide, tile, row, col))
matrix[row][col] = tile
screen.mainloop()
If you click on a number tile that's next to the blank space, it will move into the blank space, otherwise nothing happens. This code doesn't guarantee a solvable puzzle -- half won't be solvable due to the random shuffle. It's just a demonstration, the fine details of it, and the tiles themselves, are left to you.

Related

Conway Game Of Life - How to detect Evolution has stopped

I've gotten the Conway Game of Life in Python 3.9 from git-hub (there's also a great video on youtube here, made by the author)
All works fine.
Now I'd like to auto-detect when the evolution gets stuck, that is when the cell and cell group shapes are all static or regular oscillators (see pics attached at the end of this post).
My first idea was to compare the next generation grid backwards to 4 generations, that is: if the next generation grid is equal to ny of the previous four generation then it's safe to assume that the evolution has stopped.
So I first thought to make copies of the last 4 grids in 4x2D indipendent arrays and compare at each passage the next generation with each of them.
What would be the most efficient (in terms of time lag) way to compare two bidimentional arrays with say 400 columns and 200 rows?
The scripts use pygame.
here is a copy of what I am using (my plan is to use the check_if_over function in the grid.py module to return True if the evolution has stopped).
MAIN MODULE:
modules = ["pygame", "numpy", "ctypes"]
import sys
import importlib
import subprocess
def install(package):
# install packages if needed
subprocess.check_call([sys.executable, "-m", "pip", "install", package])
# check for needed pacjakes/modules
for needed_module in modules:
try:
i = importlib.import_module(needed_module)
print(f"{needed_module} successfully imported")
except ImportError as e:
install(needed_module)
i = importlib.import_module(needed_module)
except:
"Something went wrong"
import pygame
import os
import grid
import ctypes
from pygame.locals import *
## ADJUSTABLE PARMAS
reduction_factor = 2 # reduces the width/height of the window vs screen size
fps = 60 # max frames per second
scaler = 5 # scales down, the smaller the scaler the greater the number of cells
offset = 1 # thickness of grid separator, must be < scaler
#### COLORS
black = (0, 0, 0)
white = (255, 255, 255)
blue = (0, 14, 71)
os.environ["SDL_VIDEO_CENTERED"] = '1'
user32 = ctypes.windll.user32
screensize = user32.GetSystemMetrics(0), user32.GetSystemMetrics(1)
width, height = int(screensize[0]/reduction_factor), int(screensize[1]/reduction_factor)
size = (width, height)
pygame.init()
pygame.display.set_caption("Conway Game of Life")
screen = pygame.display.set_mode(size)
clock = pygame.time.Clock()
Grid = grid.Grid(width, height, scaler, offset)
Grid.random2d_array()
run = True
# game loop
while run:
clock.tick(fps)
screen.fill(black)
for event in pygame.event.get():
if event.type == QUIT:
run = False
Grid.Conway(off_color=white, on_color=blue, surface=screen)
pygame.display.update()
pygame.quit()
exit(0)
grid.py:
import pygame
import numpy as np
import random
class Grid:
def __init__(self, width, height, scale, offset):
self.scale = scale
self.columns = int(height / scale)
self.rows = int(width / scale)
self.size = (self.rows, self.columns)
self.grid_array = np.ndarray(shape=(self.size))
# next 3 lines defines the set of array copies to save
# the past 4 generations
self.grid_array_copies = []
for i in range(0, 4):
self.grid_array_copies.append(np.ndarray(shape=(self.size)))
self.offset = offset
self.alive = 0
self.evolution_stopped = False
def random2d_array(self):
for x in range(self.rows):
for y in range(self.columns):
self.grid_array[x][y] = random.randint(0, 1)
def Conway(self, off_color, on_color, surface):
for x in range(self.rows):
for y in range(self.columns):
y_pos = y * self.scale
x_pos = x * self.scale
if self.grid_array[x][y] == 1:
pygame.draw.rect(surface, on_color, [x_pos, y_pos, self.scale - self.offset, self.scale - self.offset])
else:
pygame.draw.rect(surface, off_color, [x_pos, y_pos, self.scale - self.offset, self.scale - self.offset])
next = np.ndarray(shape=(self.size))
self.alive = 0
for x in range(self.rows):
for y in range(self.columns):
state = self.grid_array[x][y]
neighbours = self.get_neighbours(x, y)
if state == 0 and neighbours == 3:
next[x][y] = 1
self.alive += 1
elif state == 1 and (neighbours < 2 or neighbours > 3):
next[x][y] = 0
else:
next[x][y] = state
self.alive += state
self.grid_array = next
self.check_if_over(next)
with open("survivors.txt", "w") as f:
f.write(str(self.alive))
def get_neighbours(self, x, y):
total = 0
for n in range(-1, 2):
for m in range(-1, 2):
x_edge = (x + n + self.rows) % self.rows
y_edge = (y + m + self.columns) % self.columns
total += self.grid_array[x_edge][y_edge]
total -= self.grid_array[x][y]
return total
def check_if_over(self, next):
pass
Thanx for the patience.
Initial conditions:
Evolution stopped:
EDIT
I forgot to mention something that may be not that straightforward.
Unlike Golly (another open source for the Conway's Game of Life), the environment where this Game of Life plays is a finite one (see pictures above), that is it's kinda rendering of a spherical suface into a rectangle, so the cells colonies evolving past the right edge of the window re-enter at the left edge, those at the bottom edge re-enter at the top. While in Golly, for example the plan environment is theoretically infinite.
Golly starting conditions, zoomed in:
Golly starting conditions partially zoomed out (could go futher out until cells invisibility and further). The black surface is the environment, the white squares the cells

Python + Pyglet: Efficiently updating the image used by a sprite

As the title says. Using batch drawing I get really good performance, even with 4096 sprites. However, since my sprites need to change their underlying image I run into issues with performance. I'm pretty sure I'm doing something silly here, since I specifically created a grid/sprite sheet to handle this effectively. But, of course, I never really use it in any effective manner. I might as well have had 5 different images.
What I really want is to keep the underlying sprite image constant, but shift the visible part based on the "food" metric. Here's the code:
import sys, pyglet, random, time
# Constants.
WIDTH = 1280
HEIGHT = 960
TARGET_FPS = 60
GROWTH_CHANCE = 0.1
fps = 0
screen = pyglet.window.Window(WIDTH, HEIGHT)
random.seed(time.time())
# Here we load universal assets, images, sounds, etc.
grass_tiles_img = pyglet.image.load('grass_tiles.png')
grass_tiles_grid = pyglet.image.ImageGrid(grass_tiles_img, 1, 5)
# Sprite batches.
grass_batch = pyglet.graphics.Batch()
class GrassTile:
'''Define a grass tile which cows can graze on.'''
def __init__(self, x, y, food):
self.food = food
self.sprite = pyglet.sprite.Sprite(grass_tiles_grid[0], x, y,
batch=grass_batch)
def draw(self):
grid_index = (self.food // 20)
self.sprite.image = grass_tiles_grid[grid_index]
return self.sprite
def grow(self):
if random.random() < GROWTH_CHANCE:
self.food = min(self.food + 1, 99)
#screen.event
def on_close():
sys.exit()
#screen.event
def on_draw():
# Clear the screen.
screen.clear()
# Draw grass.
grass_sprites = []
for grass in grass_tiles:
grass_sprites.append(grass.draw())
grass_batch.draw()
# Draw FPS counter.
label = pyglet.text.Label('FPS: ' + str(fps), 'Times New Roman', 12, 10, 10)
label.draw()
def grow_grass(dt):
for grass in grass_tiles:
grass.grow()
def calculate_fps(dt):
global fps
fps = round(min(pyglet.clock.get_fps(), TARGET_FPS))
grass_tiles = [GrassTile(20 * i, 15 * j, 0) for j in range(64) for i in range(64)]
pyglet.clock.schedule_interval(grow_grass, 1 / TARGET_FPS)
pyglet.clock.schedule_interval(calculate_fps, 1 / TARGET_FPS)
pyglet.app.run()
And here's the image so you can run the code:
https://i.imgur.com/kFe91aA.png
Why not just have the image change during grass.grow()?
You don't need to do anything to the grass in the draw phase except draw the batch. Setting the image of a sprite isn't a draw operation, it just changes texture coordinates.
def grow(self):
if random.random() < GROWTH_CHANCE:
self.food = min(self.food + 1, 99)
grid_index = (self.food // 20)
self.sprite.image = grass_tiles_grid[grid_index]
You also shouldn't be recreating the label every draw frame. Create the label beforehand and just update the text. label.text = f'FPS: {fps}'

How to plot out grid lines in Python Turtle module?

so currently i'm trying to plot out a block maze by reading from a .txt file and then displaying it in Python's Turtle Library. Currently, my code is only able to draw out the boxes, but not the grid lines surrounding the boxes. Is there anyway to deal with this problem, cuze i tried to see the docs and they only suggested turtle.Turtle.fillcolor, which doesnt really seems right.
Here's my current code:
# import the necessary library
import turtle
# read the .txt file here!
with open("map01.txt") as f:
content = f.readlines()
content = [x.strip() for x in content]
# create the map here!
window = turtle.Screen()
window.bgcolor("white") # set the background as white(check if this is default)
window.title("PIZZA RUNNERS") # create the titlebar
window.setup(700,700)
# create pen
class Pen(turtle.Turtle):
def __init__(self):
turtle.Turtle.__init__(self)
self.shape("square")
self.color('grey')
self.penup()
self.speed(0)
# create maze_list
maze = []
# add the maze to maze
maze.append(content)
# create conversion from the list to the map in turtle
def setup_maze(level):
for y in range(len(level)):
for x in range(len(level[y])):
# get the character at each x,y coordinate
character = level[y][x]
# calculate the screen x, y coordinates
screen_x = -288 + (x * 24)
screen_y = 288 - (y * 24)
# check if it is a wall
if character == "X":
pen.goto(screen_x, screen_y)
pen.stamp()
# create class instances
pen = Pen()
# set up the maze
setup_maze(maze[0])
# main game loop
while True:
pass
And this is how the current text file i am reading from looks like:
XXXXXXXXXXXX
X.........eX
X.XXX.XXX..X
X.XsX.X.XX.X
X.X......X.X
X.XXXXXXXX.X
X..........X
XXXXXXXXXXXX
The 's' and 'e' are supposed to represent the starting point and the ending point, which is not implemented yet, so it can be ignored. The dots represent the path, and the X represents walls. The current map(or txt) dimension is 8 rows by 12 columns.
Right now my output looks like this:
I wanted it to look something like this(referring to the addition of grids, not the same pattern maze):
Assuming what you desire is:
Then we need to resize the square cursor from its default size of 20 to your tile size of 24:
from turtle import Screen, Turtle
TILE_SIZE = 24
CURSOR_SIZE = 20
class Pen(Turtle):
def __init__(self):
super().__init__()
self.shape('square')
self.shapesize(TILE_SIZE / CURSOR_SIZE)
self.color('grey')
self.penup()
self.speed('fastest')
def setup_maze(level):
''' Conversion from the list to the map in turtle. '''
maze_height, maze_width = len(level), len(level[0])
for y in range(maze_height):
for x in range(maze_width):
# get the character at each x,y coordinate
character = level[y][x]
# check if it is a wall
if character == 'X':
# calculate the screen x, y coordinates
screen_x = (x - maze_width) * TILE_SIZE
screen_y = (maze_width - y) * TILE_SIZE
pen.goto(screen_x, screen_y)
pen.stamp()
screen = Screen()
screen.setup(700, 700)
screen.title("PIZZA RUNNERS")
maze = []
with open("map01.txt") as file:
for line in file:
maze.append(line.strip())
pen = Pen()
setup_maze(maze)
screen.mainloop()
If you're looking for something more like:
Then change the line:
self.color('grey')
in the code above to:
self.color('black', 'grey')
Finally, if you desire:
Then we need to make a couple of small changes to the code above:
class Pen(Turtle):
def __init__(self):
super().__init__()
self.shape('square')
self.shapesize(TILE_SIZE / CURSOR_SIZE)
self.pencolor('black')
self.penup()
self.speed('fastest')
def setup_maze(level):
''' Conversion from the list to the map in turtle. '''
maze_height, maze_width = len(level), len(level[0])
for y in range(maze_height):
for x in range(maze_width):
# get the character at each x,y coordinate
character = level[y][x]
# check if it is a wall or a path
pen.fillcolor(['white', 'grey'][character == 'X'])
# calculate the screen x, y coordinates
screen_x = (x - maze_width) * TILE_SIZE
screen_y = (maze_width - y) * TILE_SIZE
pen.goto(screen_x, screen_y)
pen.stamp()

How to know the pixel size of a specific text on turtle graphics in python?

As title, when I execute the following code
import turtle
turtle.write("some text")
I want to know the whole size (including height and width) of the string some text on the canvas of the turtle graphics.
How can I do that?
The font size only tells you half of what you need to know, i.e. the height:
The size of a font is typically taken to be the distance from the top
of the highest character to the bottom of the lowest character.
From FontSize.html
But we can get the width via setting the move= option of turtle.write() to True. Here's a example where I want to draw a box around the text I've just drawn:
from turtle import Turtle, Screen
from tkinter.font import Font
TEXT = "Penny for your thoughts" # arbitrary text
POSITION = (150, 150) # arbitrary position
FONT_SIZE = 36 # arbitrary font size
FONT = ('Arial', FONT_SIZE, 'normal') # arbitrary font
X, Y = 0, 1
def box(turtle, lower_left, upper_right):
""" Draw a box but clean up after ourselves """
position = turtle.position()
isdown = turtle.isdown()
if isdown:
turtle.penup()
turtle.goto(lower_left)
turtle.pendown()
turtle.goto(upper_right[X], lower_left[Y])
turtle.goto(upper_right)
turtle.goto(lower_left[X], upper_right[Y])
turtle.goto(lower_left)
turtle.penup()
turtle.setposition(position)
if isdown:
turtle.pendown()
screen = Screen()
marker = Turtle(visible=False)
marker.penup()
marker.goto(POSITION)
start = marker.position()
marker.write(TEXT, align='center', move=True, font=FONT)
end = marker.position()
font_config = Font(font=FONT)
font_ascent = font_config.metrics('ascent')
buffer = (font_config.metrics('linespace') - font_ascent) / 2
# Since it's centered, the end[X] - start[X] represents 1/2 the width
box(marker, (2 * start[X] - end[X], start[Y] - buffer), (end[X], start[Y] + font_ascent + buffer))
screen.exitonclick()
Now, here's an example that draws the box first, fills it, and then draws the text into it:
from turtle import Turtle, Screen
from tkinter.font import Font
TEXT = "Penny for your thoughts" # arbitrary text
POSITION = (150, 150) # arbitrary position
FONT_SIZE = 36 # arbitrary font size
FONT = ('Arial', FONT_SIZE, 'normal') # arbitrary font
X, Y = 0, 1
# def box(turtle, lower_left, upper_right):
# """ same as above example """
def box(turtle, lower_left, upper_right):
""" Draw a box but clean up after ourselves """
position = turtle.position()
isdown = turtle.isdown()
if isdown:
turtle.penup()
turtle.goto(lower_left)
turtle.pendown()
turtle.goto(upper_right[X], lower_left[Y])
turtle.goto(upper_right)
turtle.goto(lower_left[X], upper_right[Y])
turtle.goto(lower_left)
turtle.penup()
turtle.setposition(position)
if isdown:
turtle.pendown()
screen = Screen()
font_config = Font(font=FONT)
font_ascent = font_config.metrics('ascent')
buffer = (font_config.metrics('linespace') - font_ascent) / 2
text_width = font_config.measure(TEXT)
marker = Turtle(visible=False)
marker.penup()
marker.fillcolor('pink')
marker.goto(POSITION)
# Since it's centered, we need to work with half widths
half_width = text_width / 2
marker.begin_fill()
box(marker, (POSITION[X] - half_width, POSITION[Y] - buffer), (POSITION[X] + half_width, POSITION[Y] + font_ascent + buffer))
marker.end_fill()
marker.write(TEXT, align='center', font=FONT)
screen.exitonclick()
The default font for turtle.write is 'Arial' with a font-size of 8px as described in the documentation https://docs.python.org/3/library/turtle.html#turtle.write.
The height and width of the text is dependent on the font parameter.
Though Brandon pretty much gave the answer, let me give you an example:
>>>>import turtle
>>>>turtle.write('hey hello this is DSP',font=('consolas',8,'bold'))
This would do your job, Consolas is the font type, 8 is the font's size, and bold is the font type. Other than bold you can go for italic or normal as well.

Find the color of a rectangle box in Python using graphics.py

I am designing a game using graphics.py in python. I initially got everything setup except, the game consists of clicking on a box which would flip the color of the box. eg: if the color of the box clicked was white, it would turn black. My code works for turning the white boxes to black, but wont convert the black boxes to white. I know my if statement in while loop is wrong. I want to know how you could get the value of the color of the rectangle in graphics.py so I could make a proper if statement.
# _______________________IMPORTS_________________________
from graphics import *
import random
#________________________________________________________
win = None
m_board = []
# Description:
# Wait for the user to enter a valid move via the mouse. If the player selects a position
# outside the valid range or selects an occupied board space, the player is asked again.
# The function returns the move only when it's valid.
# Return value:
# An integer in the range 0 to 99 representing the move
def make_move():
pos = win.getMouse()
x_axis = pos.x // 50
y_axis = pos.y // 50
move = y_axis * 10 + x_axis
return move
# Description:
# Creating the initial board with random black and white boxes
# Return Value:
# None
def draw_board():
global win, m_board
color = ["white", "black"] #Creating list for the random black/white
win = GraphWin("LOGICX", 500, 600)
for y in range(0, 500, 50):
for x in range(0, 500, 50):
board_box = Rectangle(Point(x, y), Point(x + 50, y + 50))
#Setting the boxes with random black/white
board_box.setFill(color[random.randint(0, 1)])
#Adding each box to the empty list
m_board.append(board_box)
#Setting outline color to differentiate individual boxes
board_box.setOutline("grey")
board_box.draw(win)
game_running = True
while game_running:
move = make_move()
if m_board[move] == "black":
m_board[move].setFill("white")
else:
m_board[move].setFill("black")
There are two significant problems with this code. The first is this line:
if m_board[move] == "black":
You know that m_board is a list of Rectangle instances, why would you expect it to be equal to "black"? We can get at the fill color this way:
if m_board[move].config["fill"] == "black":
Another approach would have been to wrap the Rectangle instances with your own object class that keeps track of things you need to query/change. The second problem is this combination:
x_axis = pos.x // 50
y_axis = pos.y // 50
move = y_axis * 10 + x_axis
...
m_board[move].setFill("white")
Although double slash (//) does non-remainder division, since pos.x & pos.y are float, the result is a float and using a float list index, i.e m_board[move], causes an error. The simple fix is to have make_move() call int() on what it returns.
My rework of all the code:
import random
from graphics import *
WINDOW_WIDTH, WINDOW_HEIGHT = 500, 500
COLUMNS, ROWS = 10, 10
TILE_WIDTH, TILE_HEIGHT = WINDOW_WIDTH // COLUMNS, WINDOW_HEIGHT // ROWS
COLORS = ["white", "black"] # Creating list for the random black/white
def make_move():
pos = win.getMouse()
x_axis = pos.x // TILE_WIDTH
y_axis = pos.y // TILE_HEIGHT
return int(y_axis * COLUMNS + x_axis)
def draw_board():
board = []
for y in range(0, WINDOW_HEIGHT, TILE_HEIGHT):
for x in range(0, WINDOW_WIDTH, TILE_WIDTH):
board_box = Rectangle(Point(x, y), Point(x + TILE_WIDTH, y + TILE_HEIGHT))
# Set the boxes with random black/white
board_box.setFill(random.choice(COLORS))
# Set outline color to differentiate individual boxes
board_box.setOutline("grey")
# Add each box to the empty list
board.append(board_box)
board_box.draw(win)
return board
win = GraphWin("LOGICX", WINDOW_WIDTH, WINDOW_HEIGHT)
m_board = draw_board()
game_running = True
while game_running:
move = make_move()
if m_board[move].config["fill"] == "black":
m_board[move].setFill("white")
else:
m_board[move].setFill("black")

Categories

Resources