I want to alternate between X's and O's in pygame. I have made a while loop that should restart if the image has been blitted. It's just that it keeps returning the same image instead of alternating to X or O. I can't seem to find what's wrong. I have involved the use of next(alternator) to split the decision between X and O.
import matplotlib.pyplot as plt
import pygame
import sys
import pygame
import os
from PIL import Image
pygame.font.init()
size = 320, 240
black = 0, 0, 0
white = 255,255,255
red = 255, 0, 0
x1y1 = [(100, 0), (100, 300)]
x2y2 = [(200, 0), (200, 300)]
x3y3 = [(0, 100), (300, 100)]
x4y4 = [(0, 200), (300, 200)]
ser = []
for a in range(0,3):
for b in range(0,3):
ser.append((a,b))
def centroid(coord1, coord2):
xx = 50
yy = 50
coords = []
for a in range(0,3):
for b in range(0,3):
if a == int(coord1) and b == int(coord2):
coords += tuple([xx + a*100, yy + b*100])
return tuple(coords)
def fourCorners(a,b,length,width):
center = (a, b)
corner3 = (int(a + length/2), int(b + width/2))
corner2 = (int(a + length/2), int(b - width/2))
corner4 = (int(a - length/2), int(b + width/2))
corner1 = (int(a - length/2), int(b - width/2))
return [corner1 ,corner2 ,corner3 ,corner4]
def withinRect(a,b,corners):
if len(corners) != 4:
print('Pass a list parameter of length 4.')
elif int(corners[0][0]) >= int(a) >= int(corners[1][0]) and int(corners[0][1]) >= int(b) >= int(corners[1][1]):
return True
screen = pygame.display.set_mode((300,300))
screen.fill(white)
pygame.draw.line(screen, (0, 0, 128), x1y1[0], x1y1[1], 3)
pygame.draw.line(screen, (0, 0, 128), x2y2[0], x2y2[1], 3)
pygame.draw.line(screen, (0, 0, 128), x3y3[0], x3y3[1], 3)
pygame.draw.line(screen, (0, 0, 128), x4y4[0], x4y4[1], 3)
os.chdir('C:\\Users\\DELL\\Documents\\E-books\\Coding\\Projects')
os.path.abspath("X.png")
ximg = pygame.image.load("X.png")
ximg = pygame.transform.scale(ximg, (80,80))
os.path.abspath("O.png")
oimg = pygame.image.load("O.png")
oimg = pygame.transform.scale(oimg, (80,80))
def insert_x():
global result
result = ()
def alternate():
while True:
yield 0
yield 1
alternator = alternate()
next(alternator)
button = pygame.Rect(0,0,300,300)
while True:
ev = pygame.event.get()
for event in ev:
if event.type == pygame.MOUSEBUTTONDOWN:
pos = pygame.mouse.get_pos()
x, y = event.pos
evb = pygame.Rect(x,y,10,10)
for val in ser:
va = tuple([100*x + 10 for x in val])
if (va[0] + 100 >= x >= va[0] and va[1] + 100 >= y >= va[1]):
result += va
if (button.colliderect(evb)):
if next(alternator) == 1:
screen.blit(oimg,[result[0], result[1]])
next(alternator)
pygame.display.flip()
result = ()
continue
elif next(alternator) == 0:
screen.blit(ximg,[result[0], result[1]])
next(alternator)
pygame.display.flip()
result = ()
continue
pygame.display.update()
pygame.display.flip()
continue
insert_x()
The problem is syncronised calling of next(alternator).
Given it yields 0/1/0/1/... forever, calling it twice resets is back to the same value - that is, it causes it to skip the second value.
Consider the code:
def alternate():
while True:
yield 0
yield 1
alternator = alternate()
next(alternator)
for i in range( 10 ):
if ( next(alternator) == 1 ):
print("O ", end='')
next(alternator)
elif ( next(alternator) == 0 ):
print("X ", end='')
next(alternator)
print("")
Which outputs:
OOOOOOOOOO
And then consider this alternative:
for i in range( 10 ):
alt = next(alternator)
if ( alt == 1 ):
print("o", end='')
elif ( alt == 0 ):
print("x", end='')
print("")
Which outputs:
oxoxoxoxox
The alternator is a pretty interesting way of handling this flip-flopping value though, I like it.
Related
I'm not managing to get this math correct, and it's a little bit difficult to explain in words. I have managed to create a isometric grid, which you can select the tiles with the mouse perfectly, and I have managed to implement a camera movement using wasd keys and still get the tiles correctly selected, but there is a slightly bug which I can not figure out where is coming from.
This is what happens, but only sometimes, depend where the camera offset is:
when this happens, it is only on the x axis, and not in every tile.
I'm almost giving up on this cause I can't find the bug, thought of posting here to see if anyone had similar problem.
import time
import pygame
import sys
import math
from os import path
from settings import *
from sprites import *
# ------------------------- SETTINGS ---------------------------- #
# COLORS (r, g, b)
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
DARKGREY = (40, 40, 40)
LIGHTGREY = (100, 100, 100)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
RED = (255, 0, 0)
YELLOW = (255, 255, 0)
# game settings
WIDTH = 1024
HEIGHT = 768
FPS = 60
title = "Isometric-Based game"
BGCOLOUR = DARKGREY
TILE_X = 80
TILE_Y = 40
WORLD_X, WORLD_Y = 14, 10
ORIGIN_X, ORIGIN_Y = 5, 1
# Debug
pygame.init()
font = pygame.font.Font(None, 25)
CAMERA_SPEED = 300
def get_info(info_list):
display_surface = pygame.display.get_surface()
for i, key in enumerate(info_list):
text = font.render(str(key) + " : " + str(info_list[key]), True, (255, 255, 255), (0, 0, 0))
text_rect = text.get_rect()
text_rect.y = 20 * i
display_surface.blit(text, text_rect)
# ------------------------- SPRITES ---------------------------- #
class Camera:
def __init__(self, game, x, y):
self.game = game
self.x, self.y = self.game.to_screen(x, y)
self.vx, self.vy = 0, 0
def update(self):
self.get_keys()
self.x += self.vx * self.game.dt
self.y += self.vy * self.game.dt
def get_keys(self):
self.vx, self.vy = 0, 0
keys = pygame.key.get_pressed()
if keys[pygame.K_w]:
self.vy = -CAMERA_SPEED
if keys[pygame.K_s]:
self.vy = CAMERA_SPEED
if keys[pygame.K_a]:
self.vx = -CAMERA_SPEED
if keys[pygame.K_d]:
self.vx = CAMERA_SPEED
if self.vx != 0 and self.vy != 0:
self.vx *= 1.0
self.vy *= 0.50
class MouseSelection:
def __init__(self, game, image):
self.game = game
self.image = image
def update(self):
# get mouse x and y
self.mouse_x, self.mouse_y = pygame.mouse.get_pos()
# get the mouse offset position inside the tile
self.offset_x, self.offset_y = self.mouse_x % TILE_X, self.mouse_y % TILE_Y
self.offset_x += self.game.scroll_x % TILE_X # Add camera scroll to offset
self.offset_y += self.game.scroll_y % TILE_Y
# get the cell number
self.cell_x, self.cell_y = (self.mouse_x // TILE_X), (self.mouse_y // TILE_Y)
self.cell_x += int((self.game.scroll_x // TILE_X)) # Add camera scroll to cell
self.cell_y += int((self.game.scroll_y // TILE_Y))
# get the selected cell in iso grid
self.selected_x = (self.cell_y - ORIGIN_Y) + (self.cell_x - ORIGIN_X)
self.selected_y = (self.cell_y - ORIGIN_Y) - (self.cell_x - ORIGIN_X)
# height and width of a quarter of a tile, select the corner of the tile to nodge to a direction
h, w = TILE_Y / 2, TILE_X / 2
if self.offset_y < (h / w) * (w - self.offset_x):
self.selected_x -= 1
if self.offset_y > (h / w) * self.offset_x + h:
self.selected_y += 1
if self.offset_y < (h / w) * self.offset_x - h:
self.selected_y -= 1
if self.offset_y > (h / w) * (2 * w - self.offset_x) + h:
self.selected_x += 1
# translate the selected cell to world coordinate
self.selectedWorld_x, self.selectedWorld_y = self.game.to_screen(self.selected_x, self.selected_y)
def draw(self):
# Draw the selected tile with the camera scroll offset
self.game.screen.blit(self.image, (self.selectedWorld_x - self.game.scroll_x,
self.selectedWorld_y - self.game.scroll_y))
class SpriteSheet:
def __init__(self, image):
self.image = image
self.frames = []
def get_image(self):
for row in range(2):
for col in range(4):
if row == 0:
image = pygame.Surface((TILE_Y, TILE_Y / 2)).convert_alpha()
image.blit(self.image, (0, 0), (col * TILE_X / 2, row * TILE_Y / 2, TILE_X, TILE_Y))
image = pygame.transform.scale(image, (TILE_X, TILE_Y))
else:
image = pygame.Surface((TILE_Y, TILE_Y)).convert_alpha()
image.blit(self.image, (0, 0), (col * TILE_X / 2, row * TILE_Y / 2, TILE_X, TILE_Y * 2))
image = pygame.transform.scale(image, (TILE_X, TILE_Y * 2))
image.set_colorkey(WHITE)
self.frames.append(image)
return self.frames
# ------------------------- GAME LOOP ---------------------------- #
class Game:
def __init__(self):
pygame.init()
self.screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption(title)
self.clock = pygame.time.Clock()
pygame.key.set_repeat(400, 100)
self.debug = {}
self.sprite_sheet_image = pygame.image.load("isometric_whitebg - Copy.png")
self.index = 1
self.scroll_x, self.scroll_y = 0, 0
def new(self):
# initialize all variables and do all the setup for a new game
self.sprite_sheet = SpriteSheet(self.sprite_sheet_image)
self.tile_selected = self.sprite_sheet.get_image()[0]
self.tiles = self.sprite_sheet.get_image()
self.mouse_selection = MouseSelection(self, self.tile_selected)
self.camera = Camera(self, 1, 1)
def run(self):
# game loop - set self.playing = False to end the game
self.playing = True
while self.playing:
self.dt = self.clock.tick(FPS) / 1000
self.events()
self.update()
self.draw()
def quit(self):
pygame.quit()
sys.exit()
def update(self):
# update portion of the game loop
self.camera.update()
self.mouse_selection.update()
self.mx, self.my = pygame.mouse.get_pos()
# -------------------------------------------------- CAMERA SCROLLING ----------------------------------------#
if self.camera.x - self.scroll_x != WIDTH / 2:
self.scroll_x += (self.camera.x - (self.scroll_x + WIDTH / 2)) / 10
if self.camera.y - self.scroll_y != HEIGHT / 2:
self.scroll_y += (self.camera.y - (self.scroll_y + HEIGHT / 2)) / 10
# -------------------------------------------------- CAMERA SCROLLING ----------------------------------------#
self.debug_info()
def to_screen(self, x, y):
screen_x = (ORIGIN_X * TILE_X) + (x - y) * (TILE_X / 2)
screen_y = (ORIGIN_Y * TILE_Y) + (x + y) * (TILE_Y / 2)
return screen_x, screen_y
def draw_world(self):
for y in range(WORLD_Y):
for x in range(WORLD_X):
vWorld_x, vWorld_y = self.to_screen(x, y)
# Invisible tile
if self.index == 0:
self.screen.blit(self.tiles[1], (vWorld_x, vWorld_y))
# Grass
elif self.index == 1:
self.screen.blit(self.tiles[2], (vWorld_x - self.scroll_x, vWorld_y - self.scroll_y))
def draw(self):
self.screen.fill(BGCOLOUR)
self.draw_world()
self.mouse_selection.draw()
get_info(self.debug)
pygame.display.flip()
def debug_info(self):
self.debug["FPS"] = int(self.clock.get_fps())
self.debug["Cell"] = self.mouse_selection.cell_x, self.mouse_selection.cell_y
self.debug["Selected"] = int(self.mouse_selection.selected_x), int(self.mouse_selection.selected_y)
self.debug["Scroll"] = int(self.scroll_x), int(self.scroll_y)
self.debug["Mouse"] = int(self.mx), int(self.my)
self.debug["Mouse_offset"] = int(self.mouse_selection.offset_x), int(self.mouse_selection.offset_y)
def events(self):
# catch all events here
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.quit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
self.quit()
if event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 1:
pass
game = Game()
while True:
game.new()
game.run()
Define the corner points of the map:
map_outline = [
pygame.math.Vector2(left_x, left_y),
pygame.math.Vector2(top_x, top_y),
pygame.math.Vector2(right_x, right_y,
pygame.math.Vector2(bottom_x, bottom_y)
]
With this information you can calculate the x and y axis of the map:
origin = map_outline[0]
x_axis = (map_outline[1] - map_outline[0]) / columns
y_axis = (map_outline[3] - map_outline[0]) / rows
You can use the x-axis and the y-axis to calculate a point in the map as a function of the row and column:
def transform(p, mat2x2):
x = p[0] * mat2x2[0][0] + p[1] * mat2x2[1][0]
y = p[0] * mat2x2[0][1] + p[1] * mat2x2[1][1]
return pygame.math.Vector2(x, y)
p_position = transform((column + 0.5, row + 0.5), (x_axis, y_axis)) + origin
If you want to get the row and column depending on the mouse cursor, you need to do the opposite. You need to calculate the inverse 2x2 matrix from the x and y axis. Using the inverse matrix, you can calculate the row and column as a function of a point on the map:
def inverseMat2x2(m):
a, b, c, d = m[0].x, m[0].y, m[1].x, m[1].y
det = 1 / (a*d - b*c)
return [(d*det, -b*det), (-c*det, a*det)]
m_pos = pygame.mouse.get_pos()
m_grid_pos = transform(pygame.math.Vector2(m_pos) - origin, point_to_grid)
m_col, m_row = int(m_grid_pos[0]), int(m_grid_pos[1])
Also see PyGameExamplesAndAnswers - Isometric
Minimal example:
replit.com/#Rabbid76/Pygame-IsometircMap
import pygame
pygame.init()
window = pygame.display.set_mode((500, 300))
clock = pygame.time.Clock()
colors = {'g': (40, 128, 40), 'd': (90, 60, 40)}
tilemap = [
'gdddg',
'dgddd',
'ggddg',
'ggddg',
'ddddg',
'dgggd'
]
columns, rows = len(tilemap[0]), len(tilemap)
isometric_tiles = {}
for key, color in colors.items():
tile_surf = pygame.Surface((50, 50), pygame.SRCALPHA)
tile_surf.fill(color)
tile_surf = pygame.transform.rotate(tile_surf, 45)
isometric_size = tile_surf.get_width()
tile_surf = pygame.transform.scale(tile_surf, (isometric_size, isometric_size//2))
isometric_tiles[key] = tile_surf
tile_size = (isometric_size, isometric_size//2)
def tileRect(column, row, tile_size):
x = (column + row) * tile_size[0] // 2
y = ((columns - column - 1) + row) * tile_size[1] // 2
return pygame.Rect(x, y, *tile_size)
game_map = pygame.Surface(((columns+rows) * isometric_size // 2, (columns+rows) * isometric_size // 4), pygame.SRCALPHA)
for column in range(columns):
for row in range(rows):
tile_surf = isometric_tiles[tilemap[row][column]]
tile_rect = tileRect(column, row, tile_size)
game_map.blit(tile_surf, tile_rect)
map_rect = game_map.get_rect(center = window.get_rect().center)
map_outline = [
pygame.math.Vector2(0, columns * isometric_size / 4),
pygame.math.Vector2(columns * isometric_size / 2, 0),
pygame.math.Vector2((columns+rows) * isometric_size // 2, rows * isometric_size / 4),
pygame.math.Vector2(rows * isometric_size / 2, (columns+rows) * isometric_size // 4)
]
for pt in map_outline:
pt += map_rect.topleft
origin = map_outline[0]
x_axis = (map_outline[1] - map_outline[0]) / columns
y_axis = (map_outline[3] - map_outline[0]) / rows
def inverseMat2x2(m):
a, b, c, d = m[0].x, m[0].y, m[1].x, m[1].y
det = 1 / (a*d - b*c)
return [(d*det, -b*det), (-c*det, a*det)]
point_to_grid = inverseMat2x2((x_axis, y_axis))
def transform(p, mat2x2):
x = p[0] * mat2x2[0][0] + p[1] * mat2x2[1][0]
y = p[0] * mat2x2[0][1] + p[1] * mat2x2[1][1]
return pygame.math.Vector2(x, y)
font = pygame.font.SysFont(None, 30)
textO = font.render("O", True, (255, 255, 255))
textX = font.render("X", True, (255, 0, 0))
textY = font.render("Y", True, (0, 255, 0))
p_col, p_row = 2, 2
run = True
while run:
clock.tick(100)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_a and p_col > 0:
p_col -= 1
if event.key == pygame.K_d and p_col < columns-1:
p_col += 1
if event.key == pygame.K_w and p_row > 0:
p_row -= 1
if event.key == pygame.K_s and p_row < rows-1:
p_row += 1
p_position = transform((p_col + 0.5, p_row + 0.5), (x_axis, y_axis)) + origin
m_pos = pygame.mouse.get_pos()
m_grid_pos = transform(pygame.math.Vector2(m_pos) - origin, point_to_grid)
m_col, m_row = int(m_grid_pos[0]), int(m_grid_pos[1])
window.fill((0, 0, 0))
window.blit(game_map, map_rect)
pygame.draw.lines(window, (164, 164, 164), True, map_outline, 3)
pygame.draw.line(window, (255, 0, 0), origin, origin+x_axis, 3)
pygame.draw.line(window, (0, 255, 0), origin, origin+y_axis, 3)
pygame.draw.circle(window, (255, 255, 255), origin, 5)
pygame.draw.circle(window, (255, 0, 0), origin+x_axis, 5)
pygame.draw.circle(window, (0, 255, 0), origin+y_axis, 5)
window.blit(textO, textO.get_rect(topright = origin+(-5, 5)))
window.blit(textX, textX.get_rect(bottomright = origin+x_axis+(-5, -5)))
window.blit(textY, textX.get_rect(topright = origin+y_axis+(-5, 5)))
pygame.draw.ellipse(window, (255, 255, 0), (p_position[0]-16, p_position[1]-8, 32, 16))
if 0 <= m_grid_pos[0] < columns and 0 <= m_grid_pos[1] < rows:
tile_rect = tileRect(m_col, m_row, tile_size).move(map_rect.topleft)
pts = [tile_rect.midleft, tile_rect.midtop, tile_rect.midright, tile_rect.midbottom]
pygame.draw.lines(window, (255, 255, 255), True, pts, 4)
pygame.display.update()
pygame.quit()
exit()
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 1 year ago.
Improve this question
The cells don't behave like they're supposed to, meaning they don't follow the rules. I've tried everything I could think of, but it doesn't work.
The variable "state" is used to store the current states of all the cells (1 if active 0 if not) in the format state[x][y], similarly, "cells" also follow the same format but instead of 1s and 0s, it stores all the sprites, aka cells. The "new_state" is exactly like the state but it stores the states for the next generation. (The next generation is calculated from "state" and stored in "new_state".)
Heres an example of what "state" might look like:
state = [[0,0,0,0,0,0,0,0,0],[0,1,1,1,0,0,0], . . . ]
To calculate how many active neighbors there are next to a cell, I just did a nested for loop, both with range(-1, 2). If i, j are the variables of the for loops, then that would look something like this:
for i in range(-1, 2):
for j in range(-1, 2):
sum += state[x + i][y + j]
sum -= state[x][y]
Anyways, heres the code:
import pygame as pg
pg.init()
ticking = False
# colours
white = (255, 255, 255)
grey = (100, 100, 100)
blue = (0, 0, 255)
black = (0, 0, 0)
drk_blue = (0, 0, 100)
red = (255, 0, 0)
# now the screen
width = 750
height = 500
main_screen = pg.display.set_mode([width, height])
main_screen_rect = main_screen.get_rect()
game_width = width - width // 5
main_screen.fill(black)
game_screen = pg.Surface([game_width, height])
game_screen_rect = game_screen.get_rect()
game_screen.fill(black)
WH_cells = [0, 42]
for x in range(0, game_width, 12):
WH_cells[0] += 1
a = False # This is for toggling the eraser for the cells
# The state says which cells are active and inactive and cells is just a list containing the sprites
state = []
cells = []
new_state = []
# New state is for updating, i.e., it contains the states of the next generation
# imp functions
def logic():
sum_calc()
drawer()
global state, new_state
state = new_state
new_state = blank
def sum_calc():
global new_state
state_len = len(state)
state_len_part = len(state[0])
for x_c in range(1, state_len - 1):
for y_c in range(1, state_len_part - 1):
neigh_sum = 0
for i in range(-1, 2):
for j in range(-1, 2):
if x_c + i < state_len and y_c + j < len(state[x_c + i]):
neigh_sum += state[x_c + i][y_c + j]
neigh_sum -= state[x_c][y_c]
if neigh_sum < 2 or neigh_sum > 3:
new_state[x_c][y_c] = 0
elif neigh_sum == 3:
new_state[x_c][y_c] = 1
def drawer():
state_len = len(new_state)
state_len_part = len(new_state[0])
for x in range(state_len):
for y in range(state_len_part):
if new_state[x][y] != 1:
cells[x][y].Activate(False)
else:
cells[x][y].Activate(True)
# sprites
class Cell(pg.sprite.Sprite):
def __init__(self):
super(Cell, self).__init__()
self.surf = pg.Surface((10, 10))
self.surf.fill(grey)
self.rect = self.surf.get_rect()
self.index = None
def update(self, mouse_pos, eraser):
if self.rect.collidepoint(mouse_pos[0], mouse_pos[1]):
cell_x = self.index[0]
cell_y = self.index[1]
global state
if not eraser:
self.surf.fill(white)
state[cell_x][cell_y] = 1
else: # if eraser
self.surf.fill(grey)
state[cell_x][cell_y] = 0
def Activate(self, yesno):
global new_state
cell_x = self.index[0]
cell_y = self.index[1]
if yesno:
self.surf.fill(white)
new_state[cell_x][cell_y] = 1
else:
self.surf.fill(grey)
new_state[cell_x][cell_y] = 0
all_sprites = pg.sprite.Group()
running = True
# generating the cells and lists
for x in range(0, game_width, 12):
state.append([])
cells.append([])
for y in range(0, height, 12):
x_coord = int(x / 12)
state[x_coord].append(0)
new_cell = Cell()
new_cell.rect.x = x
new_cell.rect.y = y
new_cell.index = (x_coord, int(y / 12))
cells[x_coord].append(new_cell)
all_sprites.add(new_cell)
game_screen.blit(new_cell.surf, (x, y))
sprite_list = all_sprites.sprites()
sprite_list_len = len(sprite_list)
new_state = state
blank = state
while running:
if ticking:
logic()
for event in pg.event.get():
if event.type == pg.QUIT:
running = False
if event.type == pg.KEYDOWN:
if event.key == pg.K_a:
a = not a
if event.key == pg.K_SPACE:
if ticking:
ticking = not ticking
else:
ticking = True
print("ticking toggled to: ", ticking)
if event.type == pg.MOUSEMOTION or pg.mouse.get_pressed()[0]:
if pg.mouse.get_pressed()[0]:
for sprites in all_sprites:
sprites.update(pg.mouse.get_pos(), a)
for sprites in sprite_list:
game_screen.blit(sprites.surf, (sprites.rect.x, sprites.rect.y))
main_screen.blit(game_screen, (0, 0))
pg.display.update(main_screen_rect)
fps = pg.time.Clock()
fps.tick(60)
An assignment operation like new_state = state doesn't generate a new object. After this expression, you have 2 variables that refer to the same object.
Actually you have just 1 grid of states. The variables state, new_state and blank refer to the same object. You must create a new and empty state grid in each frame:
def logic():
global state, new_state
new_state = [[0 for _ in row] for row in state]
sum_calc()
drawer()
state = new_state
Furthermore, your algorithm is not correct. See Conway's Game of Life. Change the lgoic:
def sum_calc():
global new_state
state_len = len(state)
state_len_part = len(state[0])
for x_c in range(1, state_len - 1):
for y_c in range(1, state_len_part - 1):
neigh_sum = 0
for i in range(-1, 2):
for j in range(-1, 2):
if x_c + i < state_len and y_c + j < len(state[x_c + i]):
neigh_sum += state[x_c + i][y_c + j]
neigh_sum -= state[x_c][y_c]
#if neigh_sum < 2 or neigh_sum > 3:
# new_state[x_c][y_c] = 0
#elif neigh_sum == 3:
# new_state[x_c][y_c] = 1
if state[x_c][y_c] == 1 and (neigh_sum == 2 or neigh_sum == 3):
new_state[x_c][y_c] = 1
elif state[x_c][y_c] == 0 and neigh_sum == 3:
new_state[x_c][y_c] = 1
else:
new_state[x_c][y_c] = 0
I was wondering if it was possible to undraw previously drawn shapes from a loop. I have this function that'll create squares on click however I want to make it so that when the same area is clicked a second time the square will undraw.
from graphics import *
def createGrid():
X = 0
Y = 0
gridSize = 5
for i in range(1, gridSize + 1):
for j in range(1, gridSize + 1):
gridSquare = Rectangle(Point(X, Y), Point(X + 100, Y + 100))
gridSquare.draw(win)
X = X + 100
Y = Y + 100
X = 0
def editMode():
SelectedSquare = []
instruction = input("> ")
if instruction == "s":
selectionMode(SelectedSquare)
def selectionMode(SelectedSquare):
editSquare = Rectangle(Point(0, 0), Point(20, 20))
editSquare.setFill("black")
editSquare.draw(win)
while True:
selection = win.getMouse()
clickXPos = selection.getX()
clickYPos = selection.getY()
if clickXPos > 20 and clickYPos > 20:
PosX, PosY = clickXPos - (clickXPos % 100), clickYPos - (clickYPos % 100)
SelectedSquare = SelectedSquare + [Point(PosX, PosY)]
rect = Rectangle(Point(PosX, PosY), Point(PosX + 100, PosY + 100))
rect.setWidth(5)
rect.draw(win)
else:
editSquare.undraw()
break
win = GraphWin("GRID", 500, 500)
createGrid()
while True:
editMode()
As you can see, on click, there will be a thicker border around the grid square that was selected, I would like to be able to
1) remove the thickened border if clicked a second time
2) be able to remove all thickened borders surrounding grid squares
but I just cannot seem to figure this out, any help would be greatly appreciated!
The general problem seems to be that you have no underlying data structure or logic for your program -- you're drawing the interface first and then trying to make its behaviors define the program.
Below I've patched your code to have the points on the selected squares list remember what rectangle was drawn to highlight them, so if they are selected again, the highlight can be undone and the point removed:
from graphics import *
GRID_SIZE = 5
SQUARE_SIZE = 100
EDIT_BUTTON_SIZE = 20
BORDER_WIDTH = 5
def createGrid():
X, Y = 0, 0
for _ in range(1, GRID_SIZE + 1):
for _ in range(1, GRID_SIZE + 1):
gridSquare = Rectangle(Point(X, Y), Point(X + SQUARE_SIZE, Y + SQUARE_SIZE))
gridSquare.draw(win)
X += SQUARE_SIZE
Y += SQUARE_SIZE
X = 0
def editMode():
selectedSquares = []
instruction = input("> ")
if instruction == "s":
selectionMode(selectedSquares)
def checkSelected(point, squares):
for selected in squares:
if point.getX() == selected.getX() and point.getY() == selected.getY():
return selected
return None
def selectionMode(selectedSquares):
editSquare = Rectangle(Point(0, 0), Point(EDIT_BUTTON_SIZE, EDIT_BUTTON_SIZE))
editSquare.setFill("black")
editSquare.draw(win)
while True:
selection = win.getMouse()
clickXPos = selection.getX()
clickYPos = selection.getY()
if clickXPos <= EDIT_BUTTON_SIZE and clickYPos <= EDIT_BUTTON_SIZE:
break
PosX, PosY = clickXPos - clickXPos % SQUARE_SIZE, clickYPos - clickYPos % SQUARE_SIZE
point = Point(PosX, PosY)
selected = checkSelected(point, selectedSquares)
if selected:
selected.rect.undraw()
selectedSquares.remove(selected)
else:
rect = Rectangle(point, Point(PosX + SQUARE_SIZE, PosY + SQUARE_SIZE))
rect.setWidth(BORDER_WIDTH)
rect.draw(win)
point.rect = rect
selectedSquares.append(point)
editSquare.undraw()
win = GraphWin("GRID", GRID_SIZE * SQUARE_SIZE, GRID_SIZE * SQUARE_SIZE)
createGrid()
while True:
editMode()
But this is only a band-aid -- as you add more functionality the issue of a lack of data structure and spelled out logic will continue to frustrate you.
I want to alternate between X's and O's in my tic-tac-toe game. The only problem is that after the initial click which places an O it freezes. It doesn't continue further. I expected that the while loop 'continue' from off the if statement but it's like it doesn't register any more events. Also, I used an alternator which seems to be fine, I'm reasoning that the collation of events restarts but that should just register it??
import matplotlib.pyplot as plt
import pygame
import sys
import pygame
import os
from PIL import Image
pygame.font.init()
size = 320, 240
black = 0, 0, 0
white = 255,255,255
red = 255, 0, 0
x1y1 = [(100, 0), (100, 300)]
x2y2 = [(200, 0), (200, 300)]
x3y3 = [(0, 100), (300, 100)]
x4y4 = [(0, 200), (300, 200)]
ser = []
for a in range(0,3):
for b in range(0,3):
ser.append((a,b))
def centroid(coord1, coord2):
xx = 50
yy = 50
coords = []
for a in range(0,3):
for b in range(0,3):
if a == int(coord1) and b == int(coord2):
coords += tuple([xx + a*100, yy + b*100])
return tuple(coords)
def fourCorners(a,b,length,width):
center = (a, b)
corner3 = (int(a + length/2), int(b + width/2))
corner2 = (int(a + length/2), int(b - width/2))
corner4 = (int(a - length/2), int(b + width/2))
corner1 = (int(a - length/2), int(b - width/2))
return [corner1 ,corner2 ,corner3 ,corner4]
def withinRect(a,b,corners):
if len(corners) != 4:
print('Pass a list parameter of length 4.')
elif int(corners[0][0]) >= int(a) >= int(corners[1][0]) and int(corners[0][1]) >= int(b) >= int(corners[1][1]):
return True
screen = pygame.display.set_mode((300,300))
screen.fill(white)
pygame.draw.line(screen, (0, 0, 128), x1y1[0], x1y1[1], 3)
pygame.draw.line(screen, (0, 0, 128), x2y2[0], x2y2[1], 3)
pygame.draw.line(screen, (0, 0, 128), x3y3[0], x3y3[1], 3)
pygame.draw.line(screen, (0, 0, 128), x4y4[0], x4y4[1], 3)
os.chdir('C:\\Users\\DELL\\Documents\\E-books\\Coding\\Projects')
os.path.abspath("X.png")
ximg = pygame.image.load("X.png")
ximg = pygame.transform.scale(ximg, (80,80))
os.path.abspath("O.png")
oimg = pygame.image.load("O.png")
oimg = pygame.transform.scale(oimg, (80,80))
def insert_x():
global result
result = ()
def alternate():
while True:
yield 0
yield 1
alternator = alternate()
next(alternator)
button = pygame.Rect(0,0,300,300)
while True:
ev = pygame.event.get()
for event in ev:
if event.type == pygame.MOUSEBUTTONDOWN:
pos = pygame.mouse.get_pos()
x, y = event.pos
evb = pygame.Rect(x,y,10,10)
for val in ser:
va = tuple([100*x + 10 for x in val])
if (va[0] + 100 >= x >= va[0] and va[1] + 100 >= y >= va[1]):
result += va
if (button.colliderect(evb)):
if next(alternator) == 1:
screen.blit(oimg,[result[0], result[1]])
next(alternator)
pygame.display.flip()
result = ()
continue
elif next(alternator) == 0:
screen.blit(ximg,[result[0], result[1]])
next(alternator)
pygame.display.flip()
result = ()
continue
pygame.display.update()
pygame.display.flip()
continue
insert_x()
The issue is the construct
if next(alternator) == 1:
# [...]
elif next(alternator) == 0:
# [...]
alternator is evaluated in the if statement the 1st time. If it doesn't yield 1 but yield 0, then the condition is not fulfilled and the elif statement is evaluated. But in the elif statement alternator is evaluate a second time and now it yield 1 in any case.
This cause that the elif condition is never fulfilled. The application seems to hang, because in this state neither the if condition nor the elif condition will be fulfilled at any time.
Evaluate alternate once, before the if-elif statement, to solve the issue:
a = next(alternator)
if a == 1:
screen.blit(oimg,[result[0], result[1]])
result = ()
elif a == 0:
screen.blit(ximg,[result[0], result[1]])
result = ()
By the way, the continue statement and the pygame.display.flip() calls in the event loop are superfluous. The event loop will terminate when the message queue is empty and the single pygame.display.flip() call at the end of the main llop is sufficient.
I want that when I click on one of the possible squares an X is printed.
I have made a list of all the coordinates of the tic-tac-toe grid. I've also added the centre points of each of the rectangles coordinates. I'm trying to make that if I click within the area of one of the squares that the an X button shows up. The eventual aim is to make that a double-click results in a lodged result.
import matplotlib.pyplot as plt
import pygame
import sys
import pygame
pygame.font.init()
size = 320, 240
black = 0, 0, 0
white = 255,255,255
red = 255, 0, 0
x1y1 = [(100, 0), (100, 300)]
x2y2 = [(200, 0), (200, 300)]
x3y3 = [(0, 100), (300, 100)]
x4y4 = [(0, 200), (300, 200)]
def centroid(coord1, coord2):
xx = 50
yy = 50
coords = []
for a in range(0,3):
for b in range(0,3):
if a + 1 == int(coord1) and b + 1 == int(coord2):
coords += tuple([xx + a*100, yy + b*100])
return tuple(coords)
def fourCorners(a,b,length,width):
center = (a, b)
corner3 = (int(a + length/2), int(b + width/2))
corner2 = (int(a + length/2), int(b - width/2))
corner4 = (int(a - length/2), int(b + width/2))
corner1 = (int(a - length/2), int(b - width/2))
return [corner1 ,corner2 ,corner3 ,corner4]
def withinRect(a,b,corners):
if len(corners) != 4:
print('Pass a list parameter of length 4.')
elif int(corners[0][0]) >= int(a) >= int(corners[1][0]) and int(corners[0][1]) >= int(b) >= int(corners[1][1]):
return True
screen = pygame.display.set_mode((300,300))
screen.fill(white)
pygame.draw.line(screen, (0, 0, 255), x1y1[0], x1y1[1], 3)
pygame.draw.line(screen, (0, 0, 255), x2y2[0], x2y2[1], 3)
pygame.draw.line(screen, (0, 0, 255), x3y3[0], x3y3[1], 3)
pygame.draw.line(screen, (0, 0, 255), x4y4[0], x4y4[1], 3)
while True:
ev = pygame.event.get()
for event in ev:
# handle get_pressed
if event.type == pygame.MOUSEBUTTONUP:
pos = pygame.mouse.get_pos()
x, y = event.pos
v = fourCorners(centroid(1,1)[0],centroid(1,1)[1],100,100)
button = pygame.Rect(v[1][0], v[1][1] ,100,100)
if (withinRect(x,y,v) == True and button.collidepoint(event.pos)):
print('X')
pygame.display.flip()
else:
break
pygame.display.flip()
Traverse all the 9 fields by 2 nested loos and define a pygame.Rect for the corresponding field:
for a in range(3):
for b in range(3):
button = pygame.Rect(a*100, b*100, 100, 100)
If you want you can define a margin, to limit the area to the center of a field:
margin = 4
button = pygame.Rect(a*100+margin, b*100+margin, 100-2*margin, 100-2*margin)
Use colliderect() to check if the click was inside the area:
if button.collidepoint(pos):
# [...]
Draw a line from the .bottomleft to the .topright and a line from the .topleft to the .bottomright, to form a cross:
pygame.draw.line(screen, (0, 0, 255), button.bottomleft, button.topright, 3)
pygame.draw.line(screen, (0, 0, 255), button.topleft, button.bottomright, 3)
ev = pygame.event.get()
for event in ev:
if event.type == pygame.MOUSEBUTTONUP:
pos = pygame.mouse.get_pos()
margin = 4
for a in range(3):
for b in range(3):
button = pygame.Rect(a*100+margin, b*100+margin, 100-2*margin, 100-2*margin)
if button.collidepoint(pos):
pygame.draw.line(screen, (0, 0, 255), button.bottomleft, button.topright, 3)
pygame.draw.line(screen, (0, 0, 255), button.topleft, button.bottomright, 3)