Making a sliding puzzle game using turtle (not tkinter or pygame) - python

So I'm currently trying to program a slidepuzzle game without importing tkinter or pygame. So far i've generated a board and populated it with working buttons (quit,load,reset) but I'm really lost on how to program the actual slide puzzle game with the images i've been provided.
This code generates the screen and buttons that makeup my board. clicking the load button (which i already have setup) allows the user to type in the puzzle they want to load and unscramble. The issue is that I don't know how to get all the images onto the board and Im not sure what direction i should go in to actually program the game elements (it's just a screen and buttons right now). I'm a newbie programmer so any help is really appreciated.
screen = turtle.Screen()
def generate_screen():
`os.chdir('Resources') # Changes directory to allow access to .gifs in Resources
screen.setup(700, 700)
screen.title("Sliding Puzzle Game")
screen.tracer(0)
generate_scoreboard()
generate_leaderboard()
iconturtle = turtle.Turtle()
iconturtle.penup()
for file in os.listdir():
screen.register_shape(file)
iconturtle.goto(280, -270)
iconturtle.shape('quitbutton.gif')
iconturtle.stamp()
iconturtle.goto(180, -270)
iconturtle.shape('loadbutton.gif')
iconturtle.stamp()
iconturtle.goto(80, -270)
iconturtle.shape('resetbutton.gif')
iconturtle.stamp()`
`def load_yoshi():
os.chdir('Images\\yoshi')
screen.tracer(1)
screen.register_shape('yoshi_thumbnail.gif')
t = turtle.Turtle()
t.penup()
t.shape('yoshi_thumbnail.gif')
t.goto(250,290)
t.stamp()
screen.update()
files = glob.glob('*.gif') # pulling out only .gif
images = files
print(images)
for file in images:
screen.register_shape(file)`

I've only seen turtle used to draw lines, not shapes, much less
movable game pieces. I think pygame would definitely be better for
this – OneCricketeer
Below is an example slide game simplified from an earlier answer I wrote about creating numbered tiles using turtle:
from turtle import Screen, Turtle
from functools import partial
from random import random
SIZE = 4
TILE_SIZE = 100
OFFSETS = [(-1, 0), (0, -1), (1, 0), (0, 1)]
CURSOR_SIZE = 20
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] is None:
matrix[row][col] = None
row, col = row + dy, col + dx
matrix[row][col] = tile
x, y = tile.position()
tile.setposition(x + dx * TILE_SIZE, y - dy * TILE_SIZE)
break
except IndexError:
pass
tile.onclick(partial(slide, tile, row, col))
screen = Screen()
matrix = [[None for _ in range(SIZE)] for _ in range(SIZE)]
offset = TILE_SIZE * 1.5
for row in range(SIZE):
for col in range(SIZE):
if row == SIZE - 1 == col:
break
tile = Turtle('square', visible=False)
tile.shapesize(TILE_SIZE / CURSOR_SIZE)
tile.fillcolor(random(), random(), random())
tile.penup()
tile.goto(col * TILE_SIZE - offset, offset - row * TILE_SIZE)
tile.onclick(partial(slide, tile, row, col))
tile.showturtle()
matrix[row][col] = tile
screen.mainloop()
Click on a tile next to the blank space to have it move into that space:

Related

Writing a function that draws a cube. I can't manage to make a full cube, just the y axis cube (more info in text)

def graphics ():
window = tkinter.Tk()
window.geometry("800x800") #You enter the window dimensions for
canvas = tkinter.Canvas(window, bg = 'white', width=800, height=800)
#Code for creating graphical shapes
# note for me: im going to make a graphical drawing that draws a the sizes of the cubes user wants ! (so if there's a 2x2 cube, 3x3, and 4x4, ima draw those (2d only)
user_size = input("What size cube would you like to have drawn? (3x3, 2x2, ...): ")
user_size.split()
cube_size = int(user_size[0])
cube_size += 1
counter = 0
cube_y = 800
cube_y2 = 700
cube_x = 100
cube_x2 = 200
counter = 1
e = 'red'
while (counter != cube_size):
y = canvas.create_polygon(100,cube_y,200,cube_y,200,cube_y2,100,cube_y2, fill = e, outline = 'black', width = 7)
cube_y = cube_y - 100
cube_y2 = cube_y2 - 100
print(counter)
counter+= 1
#Flips the shapes from memory onto the monitor
canvas.pack()
window.mainloop()
as the title says, I want to make a program for school that draws rubiks cube. Essentially, user enters size of cube (3x3, 4x4, ...) and the program reads the first number of the entered size. So if user writes 3x3, program will read the number '3' at the beginning.
From there, the program will run y = canvas.create_polygon(100,cube_y,200,cube_y,200,cube_y2,100,cube_y2, fill = e, outline = 'black', which creates a red block 100x100 px wide. For as many times as the loop runs, the block will go up by 100 px (it'll go up y-axis). For example, if user enters 3x3, the y code block will be made 3 times, each time it'll go up 100 px.
this is what it looks like if i enter 3x3
What I require help with is dealing with the x axis. I've managed to get the program to draw the cube on the vertical axis, but i can't manage on the horizontal axis.
My plan was to essentially have the code y = canvas.create_polygon(100,cube_y,200,cube_y,200,cube_y2,100,cube_y2, fill = e, outline = 'black' run as many times as the counter, but with each time, the x points of the polygon goes 100 px right.
So for ex. if I were to ask for 4x4 cube to be drawn, the program would firstly draw the 4 cubes going up/vertical/y-axis using the y = canvas.create_polygon() code i mentioned before, then it would draw the same shape but this time the entire code would be moved 100 px right, on the x axis, horizontally. The final product should look something like this (pardon the shitty drawing i did this on google jamboard using mouse)
If anyone has any idea on how to do so, please let me know. And apologies for the wall of text in advance! Thx!
This is the code I have written, this is to give you a start, it isn't perfect but you can adjust the sizes of the cube how you want, and the window.
I implemented a loop which loops over columns and rows, which is set to the cube size. If the cube size is set to 3, the loop will go from 1 to 3 and draw each shape. The shape is draw using canvas.create_rectangle(tileSize * col, tileSize * row, tileSize * col + tileSize, tileSize * row + tileSize, fill='red').
tileSize * col = 50*1, 50*2, 50*3 == 50, 100, 150
and it is the same for tileSize * row
the second x and y are the x and y above + tileSize which makes each rectangle 50x50 pixels. You can adjust the sizes and placement to your liking.
Adjust this part: canvas.create_rectangle(tileSize * col, tileSize * row, tileSize * col + tileSize, tileSize * row + tileSize, fill='red') to change where the cube starts to draw.
Adjust this part: canvas.create_rectangle(tileSize * col, tileSize * row, tileSize * col + tileSize, tileSize * row + tileSize, fill='red') for the size of each square.
from tkinter import *
def graphics():
window = Tk()
window.geometry("800x800") # You enter the window dimensions for
# tile/window sizes
tileSize = 50
x = tileSize * 5
y = tileSize * 5
canvas = Canvas(window, bg='white', width=x, height=y)
cols = int(input('What size cube do you want?: '))
rows = cols
for col in range(cols):
for row in range(rows):
canvas.create_rectangle(tileSize * col, tileSize * row, tileSize * col + tileSize, tileSize * row + tileSize, fill='red')
# Flips the shapes from memory onto the monitor
canvas.pack()
window.mainloop()
graphics()

Python turtle drawing equal cells in a rectangle

I'm trying to use turtle in order to draw a rectangle, and then 32 equal cells inside it. But I somehow can't get it right, I don't know why.
Here is the code I've written:
import turtle, tkinter, datetime, time
turtle.setx(-400)
turtle.sety(200)
turtle.pendown()
turtle.pencolor('#00807c')
for i in range (0,4):
x = 800
if i%2 == 0:
turtle.fd(x)
else:
turtle.fd(x/2)
turtle.right(90)
def cells(position):
for i in range (0,4):
x = 100
turtle.fd(x)
turtle.right(90)
if turtle.pos() == position:
turtle.fd(x)
position = turtle.pos()
for j in range(0, 8):
cells(turtle.pos())
turtle.done()
The result is weird, it only draws three or four cells and then the program ends.
I'd be grateful if somebody could possibly help me with this problem. Thanks.
I've rewritten your code and I don't understand why you're using a function. I've used 3 loops:
loop 3(4):
loop 2(8):
loop 1(4):
The first loop repeats himself 4 times and draws the sides of 1 square
The second loop runs the first loop 8 times, so it draws 8 squares next to each other
The third loop runs the second loop 4 times, so it draws 4 lines of 8 squares.
And that makes a field of 32 cells.
My code:
import turtle, tkinter, datetime, time
turtle.penup()
turtle.hideturtle()
turtle.setx(-400)
turtle.sety(200)
turtle.pendown()
turtle.pencolor('#00807c')
turtle.speed(0)
turtle.pendown()
for i in range (4):
x = 800
if i%2 == 0:
turtle.fd(x)
else:
turtle.fd(x/2)
turtle.right(90)
for w in range (4):
for i in range (8):
for i in range (4):
x = 100
turtle.fd(x)
turtle.right(90)
turtle.forward(x)
turtle.penup()
turtle.goto(-400,turtle.ycor()-100)
turtle.pendown()
turtle.done()
PS: I've also changed a few things like:
I hid the turtle
I changed the speed (to maximum)
I added a turtle.penup()-command before I moved the turtle in the beginning, so you don't see a black line.
Kind regards
spyrolix
You can create two functions, to simplify the logic: One to draw a square, and one to position the turtle at the place to draw a square. Then, using a little bit of index arithmetic in two nested loops (one for rows, and one for columns), use the indices values, and the side length of a square to draw at the correct location:
Maybe like this:
import turtle, tkinter
def draw_square(side):
"""draws a square of side=side starting at the current turtle location
"""
turtle.pendown()
turtle.setheading(0)
for _ in range(4):
turtle.forward(side)
turtle.left(90)
turtle.penup()
def draw_grid(rows, cols):
"""positions the turtle at the correct location,
in order to draw a grid of squares
"""
for jdx in range(rows):
for idx in range(cols):
turtle.penup()
turtle.goto(startx + side*idx, starty + side*jdx)
draw_square(side)
turtle.pencolor('#00807c')
side = 20
startx, starty = 0, 0 # this can be changed,
# other locations used are relative to this starting point
turtle.penup()
turtle.goto(startx, starty)
rows, cols = 4, 8 # this can be changed
draw_grid(rows, cols)
turtle.goto(startx, starty) # return to the starting point
turtle.done()
This is a situation where I would switch from drawing to stamping to simplify the code and speed it up:
from turtle import Screen, Turtle
WIDTH, HEIGHT = 800, 400
SQUARE = 100
CURSOR_SIZE = 20
def cells(t):
x, y = t.position()
for dy in range(HEIGHT // SQUARE):
turtle.goto(x, y + dy * SQUARE)
for dx in range(WIDTH // SQUARE):
turtle.stamp()
turtle.forward(SQUARE)
screen = Screen()
turtle = Turtle()
turtle.hideturtle()
turtle.shape('square')
turtle.shapesize(HEIGHT / CURSOR_SIZE, WIDTH / CURSOR_SIZE)
turtle.color('#00807c', 'white')
turtle.speed('fastest')
turtle.penup()
turtle.stamp() # draw outer perimeter
turtle.shapesize(SQUARE / CURSOR_SIZE)
turtle.goto(SQUARE/2 - WIDTH/2, SQUARE/2 - HEIGHT/2)
cells(turtle) # draw inner squares
screen.exitonclick()
I'd also remove magic numbers from the body of the code and declare them at the start to make them easier to adjust.

Speed up double for loop PyGame draw

I have a pygame game and it is currently bottlenecking in the Draw to screen process. This is the code (pg is pygame):
def draw_living_cells(self):
self.screen.fill(BLACK)
for x in range(0, GRID_WIDTH + 1):
for y in range(0, GRID_HEIGHT):
if self.grid[x + 1][y + 1] == 1:
pos = (int(x * CELL_SIZE), int(y * CELL_SIZE), int(CELL_SIZE), int(CELL_SIZE))
pg.draw.rect(self.screen, LIFE_COLOR, pos, 0)
pg.display.flip()
I thought multiprocessing could help, but I'm not sure how to implement, if it is possible (due to possible shared memory issues) or if it would help at all.
This process takes about 20ms with a self.grid of size 200x150 in a 800x600 display. I think its odd to have ~50fps in such a simple process.
Use pygame.PixelArray for direct pixel access of the target Surface. Set the pixels directly, instead of drawing each cell separately by pygame.draw.rect():
def draw_living_cells(self):
self.screen.fill(BLACK)
pixel_array = pg.PixelArray(self.screen)
size = self.screen.get_size()
for x in range(0, GRID_WIDTH + 1):
for y in range(0, GRID_HEIGHT):
if self.grid[x + 1][y + 1] == 1:
rect = pygame.Rect(x * CELL_SIZE, y * CELL_SIZE, CELL_SIZE, CELL_SIZE)
pixel_array[rect.left : rect.right, rect.top : rect.bottom] = LIFE_COLOR
pixel_array.close()
pg.display.flip()
I would suggest that you modify your code to use pygame's Sprite mechanics and in particular look at the sprite group pygame.sprite.DirtySprite. You can then mark the cells that have changed as dirty and have it only redraw those cells instead of all the cells.
This change would also require you to not redraw the entire background with the self.screen.fill(BLACK).
Since your method is named draw_living_cells(), that implies that there are dead cells that you do not redraw. Since you would not be filling the entire background that means that you have to draw the background onto the screen where the dead cell used to be.
This of course only helps if some the of the cells do not change each pass. Otherwise you are just adding overhead without saving drawing.
Though I recommend Sprites and pygame.sprite.DirtySprite, you can of course do something similar yourself by just marking your cells and doing that yourself in your redraw.
Finally, I tried something similar to what Glenn suggested. I used numpy to obtain where the living cells are and then iterate through those coordinates instead of the whole grid. This gave 2.5~3x performance increase. New Code:
def draw_living_cells(self):
self.screen.fill(BLACK)
living_cells_xy = np.where(self.grid == 1)
living_cells = len(living_cells_xy[0])
for i in range(0, living_cells):
x = living_cells_xy[0][i] - 1
y = living_cells_xy[1][i] - 1
pos = (int(x * CELL_SIZE), int(y * CELL_SIZE), int(CELL_SIZE), int(CELL_SIZE))
pg.draw.rect(self.screen, LIFE_COLOR, pos, 0)
pg.display.flip()

How to show two lines overlapping in turtle graphics

Screenshot of output image
I made a random walk program using turtle and I want it show where the two turtles cross paths by changing the color.
`import turtle as T
import random as R
t = T.Turtle()
u = T.Turtle()
t.speed(0)
u.speed(0)
t.hideturtle()
u.hideturtle()
t.color("red")
u.color("blue")
def randWalk(num):
for i in range(0, num):
x = R.choice((-1,1))
X = R.choice((-1,1))
y = R.choice((-1,1))
Y = R.choice((-1,1))
t.forward(x)
u.forward(X)
if y == 1:
t.left(90)
t.forward(1)
else:
t.right(90)
t.forward(1)
if Y == 1:
u.left(90)
u.forward(1)
else:
u.right(90)
u.forward(1)
randWalk(4000)`
Turtle can't interrogate what color is currently on the screen, so one way to approach this might be to have some sort of backing store where you keep track of what color was written to what pixel. Here's a rough example using Python lists:
from turtle import Screen, Turtle
from random import choice
WIDTH, HEIGHT = 300, 300
PEN_COLORS = ['red', 'blue']
OVERLAP_COLOR = 'green'
def randWalk(number):
for _ in range(number):
for turtle in turtles:
direction = choice((-1, 1))
turtle.forward(direction)
x, y = map(round, turtle.position())
old_color = color = turtle.pencolor()
turtle.undo() # undo forward()
if grid[y][x] and grid[y][x] != color:
color = OVERLAP_COLOR
turtle.pencolor(color)
turtle.goto(x, y) # redo forward()
turtle.pencolor(old_color)
grid[y][x] = color
choice((turtle.left, turtle.right))(90)
screen.update()
screen = Screen()
screen.setup(WIDTH, HEIGHT)
screen.tracer(False)
grid = [[None] * WIDTH for _ in range(HEIGHT)]
turtles = []
for color in PEN_COLORS:
turtle = Turtle()
turtle.hideturtle()
turtle.pencolor(color)
turtles.append(turtle)
randWalk(4000)
screen.tracer(True)
screen.exitonclick()
There is additional complication in the code as turtle walks a floating point plane but we need to coerce it to an integer plane to accommodate our backing store and reduce undesirable drawing artifacts.

randomly positioned circle clicking game

So I'm still very new to python and trying to learn through making small projects.
The game I'm making is meant to test your mouse accuracy by creating a bunch of random circles which the player is meant to click in a given amount of time. At the end of the game, it should tell the player their score, and how many misclicks they had.
I've been using turtle to try and do this, but I'm stuck:
import turtle
import random
t = turtle.Pen()
win = turtle.Screen()
win.bgcolor("lightgreen")
win.title("clicky")
def mycircle(red, green, blue):
t.color(red, green, blue)
t.begin_fill()
x = random.randint(10,50)
t.circle(x)
t.end_fill()
t.up()
y = random.randint(0,360)
t.seth(y)
if t.xcor() < -300 or t.xcor() > 300:
t.goto(0, 0)
elif t.ycor() < -300 or t.ycor() > 300:
t.goto(0, 0)
z = random.randint(0,100)
t.forward(z)
t.down()
for i in range(0, 20):
a = random.randint(0,100)/100.0
b = random.randint(0,100)/100.0
c = random.randint(0,100)/100.0
mycircle(a, b, c)
The main issues I've been trying to figure out are:
How can I make the circles spawn further from each other? They overlap
quite often and I want that to be avoided.
How can I make the circles spawn instantly rather than having to be
drawn?
How can I make the circles spawn further from each other?
We can keep track of circles already created and make sure their centers are at least a diameter away from each other. Your current circle placement logic is too complicated along with being faulty. Let's try to simplify it and make sure circles are drawn completely within the window.
How can I make the circles spawn instantly rather than having to be
drawn?
We could stamp them rather than draw them. However, since you are drawing so few circles, we can make every circle a turtle. This makes determining if you clicked on a circle, and removing that circle, simpler. I've added code, for you to expand on, that removes any circle that you click on:
from turtle import Turtle, Screen
from random import random, randint
CURSOR_SIZE = 20
def my_circle(color):
radius = randint(10, 50)
circle = Turtle('circle', visible=False)
circle.shapesize(radius / CURSOR_SIZE)
circle.color(color)
circle.penup()
while True:
nx = randint(2 * radius - width // 2, width // 2 - radius * 2)
ny = randint(2 * radius - height // 2, height // 2 - radius * 2)
circle.goto(nx, ny)
for other_radius, other_circle in circles:
if circle.distance(other_circle) < 2 * max(radius, other_radius):
break # too close, try again
else: # no break
break
circle.showturtle()
circle.onclick(lambda x, y, t=circle: t.hideturtle()) # expand this into a complete function
return radius, circle
screen = Screen()
screen.bgcolor("lightgreen")
screen.title("clicky")
width, height = screen.window_width(), screen.window_height()
circles = []
for _ in range(0, 20):
rgb = (random(), random(), random())
circles.append(my_circle(rgb))
screen.mainloop()
One issue you need to work out is making sure your circle color isn't too similar to (or the same as) your background color, otherwise you'll be hunting an invisible circle. Also, we might be able to speed up the circle drawing process even more, if needed.

Categories