def pen(self): turt = RawTurtle(self.screen) turt.ht() turt.speed(0) turt.up() turt.goto(-self.width, self.height) turt.down() return turt
def render_system(system, n=0): """Renders the given system using a new turtle""" # Expand if requested system.expand(n) # Draw it turtle = RawTurtle(Screen()) turtle.speed(0) turtle.ht() turtle.lt(90) system.render_turtle(turtle)
class Sierpinski: def __init__(self, x): self.it = x S = "E+D+D" for i in range(self.it): pesan = "" for j in range(len(S)): if S[j] == "E": pesan += "E+D-E-D+E" elif S[j] == "D": pesan += "DD" else: pesan += S[j] S = pesan root3 = tk.Tk() if self.it != 1: root3.title('Sierpinski Fractal with ' + str(self.it) + ' iterations') else: root3.title('Sierpinski Fractal with an iteration') self.canvas = ScrolledCanvas(master=root3, width=1000, height=1000) self.canvas.pack(fill=tk.BOTH, expand=tk.YES) screen = TurtleScreen(self.canvas) screen.screensize(10000, 10000) self.turtle = RawTurtle(screen) self.turtle.ht() self.turtle.speed(0) for i in range(len(S)): if S[i] == "E" or S[i] == "D": self.turtle.forward(10) elif S[i] == "+": self.turtle.left(120) else: self.turtle.right(120) self.canvas.bind('<MouseWheel>', self.zoom) screen.mainloop() def zoom(self, event): amount = 0.9 if event.delta < 0 else 1.1 self.canvas.scale(tk.ALL, 0, 0, amount, amount)
class Snake(): """ Implementa un Snake que puede moverse en una cuadrícula imaginaria dede m filas por n columnas. Las filas y columnas se numeran a partir de 1. El snake tendrá una largo, que incluye su cabeza. El largo tiene que ser mayor que 1. La cabeza se pinta de un color y los segmentos de su cuerpo en otro. El snake se puede mover en forma horizontal o vertical. Para mover el snake se utilizan las flechas direccionales y la tecla "x" (que también mueve el snake hacia abajo). """ ## Atributos de clase que definen el tamaño por ## defecto de la cuadrícula. FILAS = 40 COLUMNAS = 30 ## Atributos de clase que define el tamaño (en pixeles) ## de cada elemento de la cuadrícula. DIM = 10 ## Atributo de clase que defifne el tamaño por ## defecto del snake. LARGO_SNAKE = 4 ## Atributos de clase que define a partir de que ## coordenadas en el eje horizontal(x) y vertical(y) ## se empieza a dibujar la cuadrícula. DY = 20 DX = 20 @staticmethod def deme_posicion(i,j): """ Retorna la posicion superior izquierda en eje x, eje y de un elemento i,j en la cuadrícula imaginaria. Entradas: i : Fila en la cuadrícula imaginaria. j : Columna en la cuadrícula imaginaria. Salidas: (x,y) : Posición de la esquina superior izquierda en donde se encuentra la entrada (i,j) Supuesto: (i,j) es una posición válida en la cuadrícula imaginaria. """ x = Snake.DX + (j - 1) * (Snake.DIM + 1) y = Snake.DY + (i - 1) * (Snake.DIM + 1) return (x, y) def __init__(self, filas = FILAS, columnas = COLUMNAS, largo = LARGO_SNAKE, cabeza = "blue", cuerpo="red"): """Crea la culebra y su cuadrícula. Entradas: filas : # de filas en la cuadrícula imaginaria. columnas : # de columna en la cuadrícula imaginaria. largo : Tamaño del snake incluyendo la cabeza. Salidas: Snake en una cuadrícula según las dimensiones indicadas. Supuesto: La ventana, según las dimensiones establecidas, cabe en la ventana. El snake cabe en la cuadrícula. """ ## Funciones locales al constructor ## def segmento(color, i, j): """ Dibuja un segmento del snake en posicion i, j con el color indicado. Entradas: color : Color del segmento a dibujar. (i,j) : Ubicación en que se desea dibujar el segmento en la cuadrícula imaginaria. Salidas: Dibujo del segemento con el color indicado en la posición (i,j) de la cuadrícula imaginaria. Supuesto: El color es uno válido en Tkinter. (i,j) es una posición válida en la cuadrícula imaginaria. """ ## Determina la posición en los ejes ## reales de la posición (i,j) de la ## cuadrícula imaginaria. x, y = Snake.deme_posicion(i, j) ## Prepara el lápiz para dibujar ## un rectánculo relleno. self.lapiz.fillcolor(color) self.lapiz.pu() self.lapiz.setpos(x, y) self.lapiz.seth(0) self.lapiz.pd() self.lapiz.begin_fill() ## Dibuja el rectángulo con 4 ## movimientos !!! for i in range(4): self.lapiz.fd(Snake.DIM+1) self.lapiz.left(90) ## Cierra el relleno. self.lapiz.end_fill() def mueva_snake(direccion): """ Mueve el snake en la dirección indicada. Entradas: direccion : Dirección en que se mueve el snake. Salidas: Actualización del snkae en pantalla. Si el snake pega contra un borde o contra ella misma no avanza. Supuesto: dirección es alguno de los valores "Up", "Down", "Left" o "Right". """ ## Obtiene la posición actual de la cabeza del snake. ci, cj = self.snake[-1][0], self.snake[-1][1] ## Calcula la nueva posición de la cabeza según ## la dirección indicada por el usuario. if direccion == "Up": i, j = (ci if ci == 1 else ci - 1, cj) elif direccion == "Down": i, j = (ci if ci == self.filas else ci + 1, cj) elif direccion == "Left": i, j = (ci, cj if cj == 1 else cj - 1) elif direccion == "Right": i, j = (ci, cj if cj == self.columnas else cj + 1) if not((i,j) in self.snake): ## se asegura que el snake ## no choque contra sí mismo !! self.scr.tracer(False) ## Borra la cola. La cola está en la ## posición 0 de la lista self.snake. segmento("white", self.snake[0][0], self.snake[0][1]) del self.snake[0] ## Pinta la antigua cabeza de color azul para que sea ## parte del cuerpo. La cabeza es el último elemento ## de la lista self.snake. segmento(cuerpo, self.snake[-1][0], self.snake[-1][1]) ## Agrega la nueva cabeza. La cabeza nueva cabeza ## se agrega al final de la lista. self.snake.append((i, j)) segmento(cabeza, i, j) self.scr.tracer(True) def dibuja_horizontales(): """ Dibuja las filas+1 lineas horizontales. Entradas: Ninguna. Salidas: Dibujo de las líneas horizontales de la cuadrícula imaginaria en que se moverá el snake. """ dy = Snake.DY ## Posición en eje y de la primera ## línea horizontal. ## Calcula la posición en eje x en que finalizan ## todas la líneas horizontales. posFin_x = Snake.DX + self.columnas * (Snake.DIM + 1) for i in range(self.filas+1): self.lapiz.up() self.lapiz.setpos(Snake.DX, dy) self.lapiz.down() self.lapiz.setpos(posFin_x, dy) dy += Snake.DIM + 1 def dibuja_verticales(): """ Dibuja las columnas+1 lineas verticales Entradas: Ninguna. Salidas: Dibujo de las líneas verticales de la cuadrícula imaginaria en que se moverá el snake. """ dx = Snake.DX ## Posición en eje x de la primera ## línea vertical. ## Calcula la posición en eje y en que finalizan ## todas la líneas verticales. posFin_y = Snake.DY + self.filas * (Snake.DIM + 1) for j in range(self.columnas+1): self.lapiz.up() self.lapiz.setpos(dx,Snake.DY) self.lapiz.down() self.lapiz.setpos(dx, posFin_y) dx += Snake.DIM + 1 def dibuja_escenario(): """ Dibuja la cuadrícula y el snake: el cuerpo en azul y la cabeza en rojo. Entradas: Ninguna. Salidas: Dibujo de la cuadrícula imaginaria en que se moverá el snake. """ self.scr.tracer(False) ## Dibuja la cuadricula dibuja_horizontales() dibuja_verticales() ## Dibuja el cuerpo del snake for x in self.snake[:-1]: segmento(cuerpo,x[0],x[1]) ## Dibuja la cabeza segmento(cabeza, self.snake[-1][0], self.snake[-1][1]) self.scr.tracer(True) ############################################################ ## Inicio de las instrucciones del constructor __init__. ## ############################################################ ## Verifica restricciones, sino se cumplen dispara un ## SnakeError. if not (isinstance(filas,int) and isinstance(columnas,int) and \ isinstance(largo,int)): raise SnakeError("Type Error") else: ## Guarda las dimensiones de la cuadrícula ## imaginaria y del snake en atributos de instancia. self.filas = filas self.columnas = columnas self.largo = largo ## Crea la ventana y estable un título para la misma. self.root = TK.Tk() self.root.title("Snake v1.0 / 2014") ## Obtiene la posición (x,y) en la ventana de la ## última fila y columna de la cuadrícula imaginaria, ## lo anterior con el objetivo de establecer el ## tamaño de la ventana. x, y = Snake.deme_posicion(filas, columnas) ## Calcula el ancho y el alto de la ventana anchoVentana = x + Snake.DX + Snake.DIM + 1 altoVentana = y + Snake.DY + Snake.DIM + 1 ## Crea un área de dibujo (canvas) con un ancho y ## alto que está en función de la cuadrícula ## en que se moverá el snake. self.canvas = TK.Canvas(self.root, width=anchoVentana, height=altoVentana) self.canvas.pack() ## Crea un área de dibujo para tortugas. self.scr = TurtleScreen(self.canvas) ## Establece un sistema de coordenadas en donde ## el punto (0,0) se ubica en la esquina superior ## izquierda. ## El eje x va de 0 a positivos de izquierda a derecha. ## El eje y va de 0 a positivos de arriba hacia abajo. self.scr.setworldcoordinates(0,altoVentana,anchoVentana,0) self.scr.reset() ## Crea la tortuga para dibujar self.lapiz = RawTurtle(self.scr) self.lapiz.ht() ## Crea el snake. ## El snake corresponde a una lista de pares ## ordenados (x, y) en donde cada uno de estos ## elementos corresponde a un segmento del snake. ## La cabeza del snake se ubica en la última ## posición de la lista. ## El snake creado queda en posición horizontal ## y su cabeza mira hacia la derecha. La cola ## se ubica en la posición 1,1. Observe que se ## utilizaron listas por comprensión para ## construir el snake. En este punto el snake ## no es visible. self.snake = [(1,eje_y) for eje_y in range(1, self.largo + 1)] ## Dibuja la cuadrícula y el snake. dibuja_escenario() ## Establece el binding entre las teclas para el movimiento ## del snake y las funciones que atenderán dicho movimiento. ## En todos los casos se utiliza la misma función solo que ## el parámetro con que se invoca es diferente. self.scr.onkeypress(lambda : mueva_snake("Up"), "Up") # Arriba self.scr.onkeypress(lambda : mueva_snake("Right"), "Right") # Derecha self.scr.onkeypress(lambda : mueva_snake("Down"), "Down") # Abajo self.scr.onkeypress(lambda : mueva_snake("Left"), "Left") # Izquierda self.scr.onkeypress(lambda : mueva_snake("Down"), "x") # Otra vez abajo ## Se queda escuchando los eventos. ## El programa termina cuando el usuario cierre la ventana. self.scr.listen()
class TurtleCanvas(): def __init__(self,canvas): #self.window = master #self.canvas = ScrolledCanvas(master=self.window, width=800, height=600) #self.canvas.pack(fill=tk.BOTH, expand=tk.YES) self.canvas = canvas self.screen = TurtleScreen(canvas) self.turtle = RawTurtle(self.screen) self.turtle.speed("fastest") #self.window.geometry('%dx%d+%d+%d' % (cWidth, cHeight, x, y)) self.canvas.bind('<MouseWheel>', self.zoom) self.canvas.bind("<ButtonPress-1>", self.scroll_start) self.canvas.bind("<B1-Motion>", self.scroll_move) self.canvas.bind("<ButtonPress-3>", self.changeDirection) #self.canvas.bind("<c>", self.changeColor) self.rightDirection = True def changeDirection(self,event): #print(self.rightDirection) if(self.rightDirection): self.rightDirection = False else: self.rightDirection = True def changeColor(self,event): currentColorIndex = colors.index(self.turtle.color()[0]) if (currentColorIndex == (len(colors) - 1)): self.turtle.color(colors[0]) else: self.turtle.color(colors[currentColorIndex + 1]) def scroll_start(self,event): self.canvas.scan_mark(event.x, event.y) def scroll_move(self,event): self.canvas.scan_dragto(event.x, event.y, gain=1) def zoom(self,event): amount = 0.9 if event.delta < 0 else 1.1 self.canvas.scale(tk.ALL, 0, 0, amount, amount) def square(self,sidelength = 50): for i in range(4): self.turtle.forward(sidelength) self.turtle.right(90) def triangle(self,sidelength = 50): for i in range(3): self.turtle.forward(sidelength) self.turtle.right(120) def star(self,sidelength = 50): for i in range(5): self.turtle.forward(sidelength) self.turtle.right(144) def shapeDriver(self, shapeFunc, steps): self.turtle.st() i = 0 for j in range(steps): shapeFunc(1 + i) if(self.rightDirection == True): self.turtle.right(1) else: self.turtle.left(1) i += 0.1 self.turtle.ht() def helperDriver(self, shape, steps, color): print(color) self.turtle.color(color) if(shape == "Square"): self.shapeDriver(self.square,steps) if(shape == "Triangle"): self.shapeDriver(self.triangle,steps) if(shape == "Star"): self.shapeDriver(self.star,steps)
class Board: """ Area of the screen dedicated to the connect 4 game board. """ def __init__(self,screen,corners,width,height,x,y): self.corners = corners self.screen = screen self.pen = RawTurtle(screen) self.pen.speed(8) self.width = width self.height = height self.x = x self.y = y self.spaces = [] self.draw() self.draw_spaces() def draw(self): self.pen.up() self.pen.goto(self.corners[-1]) self.pen.color("#ddd") self.pen.down() self.pen.begin_fill() for i in self.corners: self.pen.goto(i) self.pen.ht() self.pen.end_fill() return def check_winner(self,space): r, c = space.idx if self.check_row(space,r,c) or \ self.check_column(space,r,c) or \ self.check_direction(space,r,c): return True return False def check_row(self,space,r,c): if space.state*4 in "".join([str(i.state) for i in self.spaces[r]]): return True def check_column(self,space,r,c): if space.state*4 in "".join([str(i[c].state) for i in self.spaces]): return True def check_direction(self,space,r,c): direct,score = [(-1,-1),(-1,1),(1,1),(1,-1)],[0,0] for i,(x,y) in enumerate(direct): idx = 0 if i % 2 == 0 else 1 score[idx] += self.check_angle(space,r,c,x,y,(x,y)) if max(score) >= 3: return True return False def check_angle(self,space,r,c,x,y,i): if r+x >= 0 and r+x < 6 and c+y >= 0 and c+y < 7: if self.spaces[r+x][c+y].state == space.state: return 1 + self.check_diag(space,r,c,x+i[0],y+i[1],i) return 0 return 0 def animate_drop(self,space): r,c = space.idx for i in range(r): self.spaces[i][c].draw() self.spaces[i][c].remove() return def space_empty(self,space): r,c = space.idx if r == len(self.spaces)-1 or self.spaces[r+1][c].state: return space for row in range(r+1,len(self.spaces)): if self.spaces[row][c].state: return self.spaces[row-1][c] return self.spaces[len(self.spaces)-1][c] def find_space(self,x,y): for row in self.spaces: cent,rad = row[0].center, row[0].radius if y > cent[1] - rad and y < cent[1] + rad: return self.search_column(row,x) return False def search_column(self,row,x): for space in row: x2 = space.center[0] if x > x2-space.radius and x < x2 + space.radius: return space return False def draw_spaces(self): row,size = [],self.width/7 radius = (size*.9)/2 x,y = self.corners[0] for j in range(6): for i in range(7): space_x = x + (size*i) space_y = y - (size*j) center = space_x+(size/2),space_y-(size/2) color = "#643" idx = (j,i) space = Space(self,center,radius,color,idx) row.append(space) self.spaces.append(row) row = [] return
class Window(Screen): """Class to generate game window, tkcanvas subclass. """ def __init__(self, color="#643", mode=1): """ Constructor for TK window. color {str} -- hex color string (default: {None}) """ super().__init__() self.player_mode = mode self.new_game(color) def new_game(self, color): """ Starts a New Game, Draws game board and creates Players. Args: color{str} - Hex-Color passed along from constructor. """ self.setup(.8, .9, 1000, 0) self.width = (self.window_width() * .9) self.height = (self.window_height() * .9) self.x = self.width / 2 self.y = self.height / 2 # ^ dimensions of game board self.font = ('Arial', 20, 'bold'), "#f50" # ^ Game Status Messages written above Game Board self.winner = None # ^ slot is adjusted once a winner is established. self.bgcolor(color) self.start_message() self.delay(8) self.tracer(2) # functions for controlling animation speed self.game_board() self.create_players() self.player.turn() def start_message(self): """ Creates Pen for drawing game update messages above the board. """ self.pen = RawTurtle(self) self.pen.ht() self.pen.up() self.pen.color(self.font[1]) self.pen.goto(0, self.y) self.pen.write("New Game", align="center", font=self.font[0]) return def create_players(self): """ Creates 2 player objects. Either AI controlled of User controlled Player_mode 1 = User vs User Player_mode 2 = User vs AI Player_mode 3 = AI vs AI """ if self.player_mode in [1, 2]: player_1 = Player(1, "#f00") if self.player_mode == 1: player_2 = Player(2, "#000") else: player_2 = Ai(2, "#000", self.board, self) else: player_1 = Ai(1, "#f00", self.board, self) player_2 = Ai(2, "#000", self.board, self) self.player = player_1 self.players = (player_1, player_2) def activate_space(self, x, y): """ Onclick callback: assigns the bottom most space to active player. Arguements: (x{int},y{int}) = window coordinates for click position. """ space = self.board.find_space(x, y) #find the space associated with the position given by onclick method if space and not space.state: space = self.board.space_empty(space) self.board.animate_drop(space) """ if space is valid... check if it is the bottommost empty space in column or return the bottom space and render dropping animation """ space.draw(color=self.player.color) space.state = self.player.name """ fill bottommost empty space with players color set space state to filled by active player """ if self.board.check_winner(space): # check if game over self.draw_message(f"GAME OVER {self.player} WINS") return # switch active players self.player = self.players[ 0] if self.players[0] != self.player else self.players[1] self.draw_message(str(self.player) + " Turn") self.player.turn() else: # if click is not in valid empty space player turn continues self.draw_message("Try Again") self.player.turn() def draw_message(self, msg): """ Write game state update messages to top of the window """ self.pen.clear() self.pen.write(msg, align="center", font=self.font[0]) return def play(self): self.onclick(self.activate_space) def game_board(self): """ Calculates and generates the board object. """ board_height = (self.height // 42) * 42 board_width = (board_height * 7) / 6 bx = board_width / 2 by = board_height / 2 board_corners = [(-bx, by), (-bx, -by), (bx, -by), (bx, by)] self.board = Board(self, board_corners, board_width, board_height, bx, by) return
class Stage: @classmethod def create(cls, screen): stage = cls(screen) start = screen.start blocks = screen.blocks for i in range(blocks): stop = start + screen.blockwidth stage.positions.append((start,stop)) base = -screen.base height = screen.increment * (i+1) color = next(screen.gradient) block = Block(screen, base=base, index=i, height=height, parent=stage, color=color) stage.blocks.append(block) block.draw() start = stop + 1 stage.get_pen() return stage def append(self, other): if other.index: other.clear() l = len(self) other.index = (l) self.blocks.append() def slice(self, *args): if len(args) > 2: raise Exception if len(args) == 2: start, stop = args if len(args) == 1: start, stop = 0, args[0] if len(args) == 0: start, stop = 0, len(self.blocks) stage = Stage(self.screen) for idx, i in enumerate(range(start,stop)): stage.positions.append(self.positions[i]) block = self.blocks[i].new() block.index = idx stage.blocks.append(block) return stage def __init__(self, screen): self.screen = screen self.positions = [] self.blocks = [] self.operations = 0 self.pen = None def get_pen(self): self.pen = RawTurtle(self.screen) self.pen.color("#f0d1bf") xpos = 0 ypos = self.screen.height - 30 self.pen.up() self.pen.ht() self.pen.goto(xpos, ypos) self.pen.down() def __getitem__(self, idx): return self.blocks[idx] def __setitem__(self, idx, other): if other.index not in [idx, None]: other.clear() self.blocks[other.index] = None if self.blocks[idx] != None: self.blocks[idx].clear() self.blocks[idx].delindex() self.blocks[idx] = other self.blocks[idx].setindex(idx) other.draw() def __str__(self): return str([i.value for i in self.blocks]) def __repr__(self): return str(self) def __len__(self): return len(self.blocks) def __iter__(self): self.iterable = iter(self.blocks) return self.iterable def __next__(self): try: block = next(self.iterable) return block except StopIteration: raise StopIteration