Why doesn't the window update in curses? - python-3.x

I took this nice example of a simple curses application with a list. I wanted to make it scrollable, so I changed the part of the list that gets shown. However, I can scroll down and back up, but the contents shown doesn't change (only the highlighted line, not the lines shown).
What am I doing wrong?
MVCE
#!/usr/bin/env python
import curses
from curses import panel
class Menu(object):
def __init__(self, items, stdscreen):
self.window = stdscreen.subwin(0, 0)
self.window.keypad(1)
self.panel = panel.new_panel(self.window)
self.panel.hide()
panel.update_panels()
self.position = 0
self.items = items
def navigate(self, n):
self.position += n
if self.position < 0:
self.position = 0
elif self.position >= len(self.items):
self.position = len(self.items) - 1
def display(self):
self.panel.top()
self.panel.show()
self.window.clear()
while True:
self.window.refresh()
curses.doupdate()
start = 0
# The next 3 lines seem not to work as intended
while start + (curses.LINES - 1) < self.position:
start += curses.LINES
for index, item in enumerate(self.items[start:curses.LINES - 1], start=start):
if index == self.position:
mode = curses.A_REVERSE
else:
mode = curses.A_NORMAL
msg = "%d. %s" % (index, item[0])
self.window.addstr(1 + index, 1, msg, mode)
key = self.window.getch()
if key in [curses.KEY_ENTER, ord("\n"), curses.KEY_RIGHT]:
self.items[self.position][1]()
elif key == curses.KEY_UP:
self.navigate(-1)
elif key == curses.KEY_DOWN:
self.navigate(1)
elif key == curses.KEY_LEFT:
break
self.window.clear()
self.panel.hide()
panel.update_panels()
curses.doupdate()
class MyApp(object):
def __init__(self, stdscreen):
self.screen = stdscreen
curses.curs_set(0)
submenu_items = [("beep", curses.beep), ("flash", curses.flash)]
submenu = Menu(submenu_items, self.screen)
main_menu_items = [
("beep", curses.beep),
("flash", curses.flash),
("submenu", submenu.display),
]
for i in range(200):
main_menu_items.append((f"flash {i}", curses.flash))
main_menu = Menu(main_menu_items, self.screen)
main_menu.display()
if __name__ == "__main__":
curses.wrapper(MyApp)

Basically that's because you're not updating the upper limit on the slice used in this loop:
for index, item in enumerate(self.items[start:curses.LINES - 1], start=start):
Here's a better version
MVCE
#!/usr/bin/env python
import curses
from curses import panel
class Menu(object):
def __init__(self, items, stdscreen):
self.window = stdscreen.subwin(0, 0)
self.window.keypad(1)
self.panel = panel.new_panel(self.window)
self.panel.hide()
panel.update_panels()
self.position = 0
self.items = items
def navigate(self, n):
self.position += n
if self.position < 0:
self.position = 0
elif self.position >= len(self.items):
self.position = len(self.items) - 1
def display(self):
self.panel.top()
self.panel.show()
self.window.clear()
while True:
start = 0
self.window.clear()
while start + (curses.LINES - 1) < self.position:
start += curses.LINES
myrow = self.position - start
mycol = 0
for index, item in enumerate(self.items[start:start + curses.LINES - 1], start=start):
if index == self.position:
mode = curses.A_REVERSE
else:
mode = curses.A_NORMAL
msg = "%d. %s" % (index, item[0])
self.window.addstr(index - start, 0, msg, mode)
if index == self.position:
(myrow, mycol) = self.window.getyx()
self.window.move(myrow, mycol)
key = self.window.getch()
if key in [curses.KEY_ENTER, ord("\n"), curses.KEY_RIGHT]:
self.items[self.position][1]()
elif key == curses.KEY_UP:
self.navigate(-1)
elif key == curses.KEY_DOWN:
self.navigate(1)
elif key == curses.KEY_LEFT:
break
self.window.clear()
self.panel.hide()
panel.update_panels()
curses.doupdate()
class MyApp(object):
def __init__(self, stdscreen):
self.screen = stdscreen
curses.curs_set(1)
submenu_items = [("beep", curses.beep), ("flash", curses.flash)]
submenu = Menu(submenu_items, self.screen)
main_menu_items = [
("beep", curses.beep),
("flash", curses.flash),
("submenu", submenu.display),
]
for i in range(200):
main_menu_items.append((f"flash {i}", curses.flash))
main_menu = Menu(main_menu_items, self.screen)
main_menu.display()
if __name__ == "__main__":
curses.wrapper(MyApp)

Related

AttibuteError when trying to make configs

AttributeError: module 'collections' has no attribute 'Sequence'
i get this error everything i try to run my code but there isn't any information about how to use Mothur except for the the documentation.
`# python3
import sys
import queue
import itertools
from collections import deque
from mothur_py import Mothur
import collections.abc as collections
class KmerIdMgmt:
def __init__(self):
self.id = 0
self.ids_map = {}
self.kmers = {}
def insert(self, kmer):
if kmer not in self.ids_map:
self.ids_map[kmer] = self.id
self.kmers[self.id] = kmer
self.id += 1
return self.ids_map[kmer]
class DeBruijnGraph(object):
def __init__(self, k, reads):
self.k = k
self.threshold = self.k + 1
self.kmer_ids = KmerIdMgmt()
self.coverage = {}
self.graph = {}
self.outgoing_num = lambda k: len(self.graph[k][0])
self.incoming_num = lambda k: self.graph[k][1]
self.make_deBruijn_graph(self.break_reads_into_kmers(reads))
def break_reads_into_kmers(self, reads):
break_read = lambda read: [ read[j:j + self.k] for j in range(len(read) - self.k + 1) ]
return [ kmer for read in reads for kmer in break_read(read) ]
def make_deBruijn_graph(self, kmers):
def add_edge(graph, coverage, left, right):
graph.setdefault(left, [set(), 0])
graph.setdefault(right, [set(), 0])
coverage.setdefault((left, right), 0)
coverage[(left, right)] += 1
if right not in graph[left][0]:
graph[left][0].add(right)
graph[right][1] += 1
for kmer in kmers:
left = self.kmer_ids.insert(kmer[:-1])
right = self.kmer_ids.insert(kmer[1:])
if left != right:
add_edge(self.graph, self.coverage, left, right)
def remove_leaves(self):
removable = [ k for k, v in self.graph.items() if len(v[0]) == 0 ]
for k in removable:
del self.graph[k]
def print_graph(self):
for k, v in self.graph.items():
print(k, v)
class TipRemoval(DeBruijnGraph):
def __init__(self, k, reads):
DeBruijnGraph.__init__(self, k, reads)
def remove_tips(self):
for k, v in self.graph.items():
find_and_remove = None
if self.outgoing_num(k) == 1 and self.incoming_num(k) == 0:
find_and_remove = self.find_and_remove_incoming
elif self.outgoing_num(k) > 1:
find_and_remove = self.find_and_remove_outgoing
else: continue
condition = True
while condition:
condition = False
for edge in v[0]:
if find_and_remove(edge, 0):
v[0].remove(edge)
condition = True
break
def find_and_remove_outgoing(self, current, depth):
if self.outgoing_num(current) > 1 or self.incoming_num(current) > 1:
return False
if depth == self.threshold:
return False
if self.outgoing_num(current) == 0:
return True
if self.find_and_remove_outgoing(next(iter(self.graph[current][0])), depth + 1):
to = next(iter(self.graph[current][0]))
self.graph[current][0].pop()
self.graph[to][1] -= 1
return True
return False
def find_and_remove_incoming(self, current, depth):
if self.outgoing_num(current) == 0 or self.incoming_num(current) > 1:
return True
if depth == self.threshold:
return False
if self.find_and_remove_incoming(next(iter(self.graph[current][0])), depth + 1):
to = next(iter(self.graph[current][0]))
self.graph[current][0].pop()
self.graph[to][1] -= 1
return True
return False
class BubbleRemoval(TipRemoval):
def __init__(self, k, reads):
TipRemoval.__init__(self, k, reads)
self.paths = {}
def remove_bubbles(self):
for k, v in self.graph.items():
if self.outgoing_num(k) > 1:
self.dfs(path=[k], current=k, depth=0)
for pair, candidates_list in self.paths.items():
source, target = pair[0], pair[1]
best_path = max(candidates_list, key=lambda item: item[1])[0]
for path, _ in candidates_list:
if best_path == path or not self.bubble_possible(source, target):
continue
if self.paths_disjoint(best_path, path) and self.path_exists(path):
self.remove_path(path)
def bubble_possible(self, source, target):
return len(self.graph[source][0]) > 1 and self.graph[target][1] > 1
def path_exists(self, path):
for j in range(len(path) -1):
if path[j +1] not in self.graph[path[j]][0]:
return False
return True
def remove_path(self, path):
for j in range(len(path) -1):
self.graph[path[j]][0].remove(path[j +1])
self.graph[path[j +1]][1] -= 1
del self.coverage[(path[j], path[j +1])]
def paths_disjoint(self, a, b):
return len(set(a) & set(b)) == 2
def dfs(self, path, current, depth):
if current != path[0] and self.incoming_num(current) > 1:
weight = sum(self.coverage[(path[i], path[i+1])] for i in range(len(path)-1)) / len(path)
self.paths.setdefault((path[0], current), list()).append((path[:], weight))
if depth == self.threshold:
return
for next_ in self.graph[current][0]:
if next_ not in path:
path.append(next_)
self.dfs(path, next_, depth + 1)
path.remove(next_)
class PhiX174GenomeAssembler(BubbleRemoval):
def __init__(self, k, reads):
BubbleRemoval.__init__(self, k, reads)
def make_Euler_cycle(self):
verteces = deque()
path = []
# line 191
current = next(iter(self.graph))
verteces.append(current)
while verteces:
current = verteces[0]
if len(self.graph[current][0]) != 0:
t = next(iter(self.graph[current][0]))
verteces.append(t)
self.graph[current][0].remove(t)
continue
path.append(current)
verteces.popleft()
return path
def assemble(self):
self.remove_tips()
self.remove_leaves()
self.remove_bubbles()
cycle = self.make_Euler_cycle()
circular_genome = self.kmer_ids.kmers[cycle[0]]
for i in range(1, len(cycle) - (self.k - 1)):
circular_genome += self.kmer_ids.kmers[cycle[i]][-1]
return circular_genome
if __name__ == "__main__":
n_kmers = int(input())
for _ in range(n_kmers):
reads = list(input())
reads = str(reads)
with open('reads.fasta', 'w') as read:
read.write(reads)
k = 100
m = Mothur()
contig = m.make.contigs(ffasta = read)
for x in range(n_kmers):
print(">CONTIG", x)
print(contig)
`

How to find the shortest path

In the function of find_shortest_func, i think if now position isn't "T" which is also known as the terminal or exit, then i will try to find for direction and see if it is "T", if not, check if it is space and i can go there. Besides, tell the next state function now output and dic to tell the place where i visited. But some errors occur and I don't know why.
I think the problem may occur where I tried to deepcopy the output list
import copy
def set_symbol(symbol_name):
def set_symbol_decorator(func):
def wrapper(self, symbol):
setattr(self, symbol_name, symbol)
return wrapper
return set_symbol_decorator
class Maze:
space_symbol = " "
obstacle_symbol = "X"
path_symbol = "•"
output = []
dis = 0
def __init__(self, input_string):
self.maze = []
if input_string.endswith("txt"):
with open(input_string) as f:
count = 0
for line in f.readlines():
self.maze.append([])
for j in line:
if j != '\n':
self.maze[count].append(j)
count += 1
else:
count = 0
for i in input_string.split("\n"):
self.maze.append([])
for j in i:
self.maze[count].append(j)
count += 1
def __str__(self):
output_string = ""
for i in range(20):
for j in range(20):
output_string += self.maze[i][j]
output_string += "\n"
return output_string
#set_symbol("space_symbol")
def set_space_symbol(self, change):
pass
#set_symbol("obstacle_symbol")
def set_obstacle_symbol(self, change):
pass
#set_symbol("path_symbol")
def set_path_symbol(self, change):
pass
def find_shortest_func(self, position: tuple, d: dict, out: list, dis: int):
dic = copy.deepcopy(d)
output = copy.deepcopy(out)
dic[(position[0], position[1])] = 1
output.append((position[0], (position[1])))
dis += 1
if self.maze[position[0]][position[1]] != "T":
if position[0]+1 < 20 and self.maze[position[0]+1][position[1]] == self.space_symbol and (position[0]+1, position[1]) not in dic:
self.find_shortest_func(
(position[0]+1, position[1]), dic, output, dis)
if position[1]+1 < 20 and self.maze[position[0]][position[1]+1] == self.space_symbol and (position[0], position[1]+1) not in dic:
self.find_shortest_func(
(position[0], position[1]+1), dic, output, dis)
if position[0]-1 >= 0 and self.maze[position[0]-1][position[1]] == self.space_symbol and (position[0]-1, position[1]) not in dic:
self.find_shortest_func(
(position[0]-1, position[1]), dic, output, dis)
if position[1]-1 >= 0 and self.maze[position[0]][position[1]-1] == self.space_symbol and (position[0], position[1]-1) not in dic:
self.find_shortest_func(
(position[0], position[1]-1), dic, output, dis)
if self.maze[position[0]][position[1]] == "T":
if dis < self.dis:
self.output = copy.deepcopy(output)
self.dis = dis
return
def find_shortest_path(self):
d = dict()
output = []
dis = -1
self.find_shortest_func((1, 0), d, output, dis)
return self.output, self.dis

How can i fix angled line error in pyqt5?

I'm implementing paint program with pyqt5.
Currently, I am making a service that targets not only fhd but also 4k.
But it is difficult to adjust in 4K (high-resolution).
In fhd, the line is not angled, but in 4k, the line is angled.
Please help me how can i fix this problem. :(
If it is difficult to understand the code, if you request additionally, I will share it on github.
class LogoClass(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowFlags(QtCore.Qt.FramelessWindowHint)
self.setAttribute(QtCore.Qt.WA_TranslucentBackground)
self.setGeometry(0, 0, APP.desktop().screenGeometry().width(), APP.desktop().screenGeometry().height())
self.image = []
self.image.append(0)
self.image[0] = QtGui.QImage(self.size(), QtGui.QImage.Format_ARGB32)
self.image[0].fill(backColor)
self.imageDraw = []
self.imageDraw.append(0)
self.imageDraw[0] = QImage(self.size(), QImage.Format_ARGB32)
self.imageDraw[0].fill(QtCore.Qt.transparent)
print("size is " + str(self.size()) + " "+str(self.imageDraw[0].rect()))
self.imageDraw2 = QImage(self.size(), QImage.Format_ARGB32)
self.imageDraw2.fill(QtCore.Qt.transparent)
self.drawing = False
# self._clear_size = 50
self.brushColor = QtGui.QColor(QtCore.Qt.black)
self.lastPoint = QtCore.QPoint()
self.beginPoint = QtCore.QPoint()
self.last_x, self.last_y = None, None
self.timer = QTimer(self)
self.timer.setInterval(2000)
self.timer.timeout.connect(self.timeout)
self.rectanglestartx = []
self.rectanglestarty = []
self.rectanglesendx = []
self.rectanglesendy = []
self.lines = []
self.drawingPath= None
self.drawingPath_pen = None
self.stack_logo = QStackedWidget(self)
self.stack_logo.setGeometry(0, 0, APP.desktop().screenGeometry().width(), APP.desktop().screenGeometry().height())
self.setWindowFlag(QtCore.Qt.Tool)
self.startPoint = None
self.endPoint = None
self.stack_logo.addWidget(QWidget())
self.max = 1
self.point = QPoint()
def paintEvent(self, event):
canvasPainter = QPainter(self)
# canvasPainter.setRenderHints(QPainter.HighQualityAntialiasing | QPainter.SmoothPixmapTransform)
#print("current index is "+str(self.stack.currentIndex()))
canvasPainter.drawImage(self.rect(), self.image[self.stack_logo.currentIndex()], self.image[self.stack_logo.currentIndex()].rect())
canvasPainter.drawImage(self.rect(), self.imageDraw[self.stack_logo.currentIndex()], self.imageDraw[self.stack_logo.currentIndex()].rect())
canvasPainter.drawImage(self.rect(), self.imageDraw2, self.imageDraw2.rect())
if self.drawingPath:
pen = QtGui.QPen(QColor(colorR, colorG, colorB, alpha), penthick, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap,
QtCore.Qt.RoundJoin)
canvasPainter.setPen(pen)
canvasPainter.drawPath(self.drawingPath)
canvasPainter.end()
def mousePressEvent(self, event):
if event.button() == QtCore.Qt.LeftButton:
if cutwidget.isVisible() == 1:
cutwidget.hide()
if checkthick.isVisible() == 1:
checkthick.hide()
if checkbackgroundcolor.isVisible() == 1:
checkbackgroundcolor.hide()
if cutoption.isVisible() == 1:
cutoption.hide()
if objectoption.isVisible() == 1:
objectoption.hide()
if logooption.isVisible() == 1:
logooption.hide()
if setting.isVisible() == 1:
setting.hide()
if undertoolbar.btn_deleteAll_check == 1:
painter = QtGui.QPainter(self.imageDraw[self.stack_logo.currentIndex()])
# print("enter deletall button")
painter.save()
painter.setCompositionMode(QtGui.QPainter.CompositionMode_Clear)
painter.eraseRect(0, 0, APP.desktop().screenGeometry().width(), APP.desktop().screenGeometry().height())
painter.restore()
elif undertoolbar.btn_hold_check == 1:
self.beginPoint = event.pos()
elif undertoolbar.btn_lightpen_check == 1:
self.drawingPath = QPainterPath()
self.drawingPath.moveTo(event.pos())
elif undertoolbar.btn_pen1_check == 1:
self.drawingPath_pen = QPainterPath()
self.drawingPath_pen.moveTo(event.pos())
elif undertoolbar.btn_eraser_check == 1:
self.changeColour()
if undostack_logo.index()==0:
undocommand_logo = UndoCommand_logo(0,0,0,0,
self.imageDraw[self.stack_logo.currentIndex()], self)
undostack_logo.push(undocommand_logo)
self.drawing = True
self.lastPoint = event.pos()
self.beginPoint = event.pos()
"""if undertoolbar.btn_cut1_check == 1:
self.beginPoint = event.pos()
self.endPoint = event.pos()
self.update()"""
def mouseMoveEvent(self, event):
if event.buttons() and QtCore.Qt.LeftButton and self.drawing:
painter = QPainter(self.imageDraw[self.stack_logo.currentIndex()])
painter.setPen(
QtGui.QPen(QColor(colorR, colorG, colorB, alpha), penthick, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap,
QtCore.Qt.RoundJoin))
painter.setRenderHints(QPainter.HighQualityAntialiasing | QPainter.SmoothPixmapTransform)
if undertoolbar.btn_pen1_check == 1:
painter.drawLine(self.lastPoint, event.pos())
# self.drawingPath_pen.lineto(event.pos())
elif undertoolbar.btn_eraser_check == 1:
# print("mouse event eraser")
# self.changeColour()
r = QtCore.QRect(QtCore.QPoint(), erasersize * QtCore.QSize())
r.moveCenter(event.pos())
painter.save()
painter.setCompositionMode(QtGui.QPainter.CompositionMode_Clear)
painter.eraseRect(r)
painter.restore()
elif undertoolbar.btn_lightpen_check == 1 and self.drawingPath:
self.changeColour()
# print("mouse event lightpen")
painter.drawLine(self.lastPoint, event.pos())
# self.drawingPath.lineTo(event.pos())
elif undertoolbar.btn_hold_check == 1:
painter2 = QtGui.QPainter(self.imageDraw2)
painter2.setPen(
QtGui.QPen(QColor(255, 0, 0, 255), 3, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap,
QtCore.Qt.RoundJoin))
painter2.setRenderHints(QPainter.HighQualityAntialiasing | QPainter.SmoothPixmapTransform)
painter2.drawLine(self.lastPoint, event.pos())
self.timer.stop()
painter2.end()
painter.end()
self.lastPoint = event.pos()
self.update()
def mouseReleaseEvent(self, event):
if undertoolbar.btn_hold_check == 1:
self.timer.start()
self.rectanglestartx.append(self.beginPoint.x())
self.rectanglestarty.append(self.beginPoint.y())
self.rectanglesendx.append(self.lastPoint.x())
self.rectanglesendy.append(self.lastPoint.y())
elif undertoolbar.btn_pen1_check == 1:
undocommand_logo = UndoCommand_logo(self.lastPoint.x(), self.lastPoint.y(), event.pos().x(),
event.pos().y(),
self.imageDraw[self.stack_logo.currentIndex()], self)
undostack_logo.push(undocommand_logo)
# painter = QPainter(self.imageDraw[self.stack_logo.currentIndex()])
# painter.setRenderHints(QPainter.HighQualityAntialiasing | QPainter.SmoothPixmapTransform)
# painter.setPen(
# QtGui.QPen(QColor(colorR, colorG, colorB, 255), penthick, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap,
# QtCore.Qt.RoundJoin))
# painter.drawPath(self.drawingPath_pen)
# painter.end()
elif undertoolbar.btn_lightpen_check == 1:
undocommand_logo = UndoCommand_logo(self.lastPoint.x(), self.lastPoint.y(), event.pos().x(),
event.pos().y(),
self.imageDraw[self.stack_logo.currentIndex()], self)
undostack_logo.push(undocommand_logo)
painter = QPainter(self.imageDraw[self.stack_logo.currentIndex()])
painter.setRenderHints(QPainter.HighQualityAntialiasing | QPainter.SmoothPixmapTransform)
painter.setPen(
QtGui.QPen(QColor(colorR, colorG, colorB, alpha), penthick, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap,
QtCore.Qt.RoundJoin))
painter.drawPath(self.drawingPath)
painter.end()
elif undertoolbar.btn_eraser_check == 1:
QApplication.setOverrideCursor(Qt.ArrowCursor)
self.drawing = False
self.drawingPath = None
self.drawingPath_pen = None
def changeColour(self):
if undertoolbar.btn_eraser_check == 1:
print("change colour eraser check")
#pixmap = QtGui.QPixmap(QtCore.QSize(1, 1) * erasersize)
#pixmap.fill(QtCore.Qt.transparent)
# cursor = QtGui.QCursor(pixmap)
pixmap = QtGui.QPixmap('./icon/eraser/8.png').scaled(erasersize, erasersize, Qt.KeepAspectRatio)
cursor = QtGui.QCursor(pixmap)
QtWidgets.QApplication.setOverrideCursor(cursor)
# print(cursor)
# painter = QtGui.QPainter(pixmap)
# painter.setPen(QtGui.QPen(QtCore.Qt.black, 2))
# painter.drawRect(pixmap.rect())
# painter.end()
# elif undertoolbar.btn_pen1_check == 1 or undertoolbar.btn_lightpen_check == 1 or undertoolbar.btn_hold_check == 1 or undertoolbar.btn_pointer_check==1 or undertoolbar.btn_deleteAll_check==1 or undertoolbar.btn_cut1_check ==1 or undertoolbar.btn_background_color_check ==1:
# print("change colour pen check")
# #QtWidgets.QApplication.restoreOverrideCursor()
# # self.setCursor(QtCore.Qt.ArrowCursor)
def timeout(self):
qp = QPainter(self.imageDraw2)
for i in range(0, len(self.rectanglesendy)):
qp.save()
qp.setCompositionMode(QtGui.QPainter.CompositionMode_Clear)
qp.eraseRect(0, 0, sizex, sizey)
qp.restore()
self.update()
qp.end()
def keyPressEvent(self, event):
global undostack_logo
if event.key() == (Qt.Key_Control and Qt.Key_Y):
undostack_logo.redo()
if event.key() == (Qt.Key_Control and Qt.Key_Z):
length = len(logoclass.lines)
if length !=0:
i = length - 1
while i != 0:
if logoclass.lines[i][0] == logoclass.lines[i - 1][2] and logoclass.lines[i][1] == \
logoclass.lines[i - 1][3]:
undostack_logo.undo()
i = i - 1
else:
print(i)
undostack_logo.undo()
i = i - 1
break

Py-Game Connect4 - Minimax (Program Halting Recursively)

main.py
from Player import Player
import tkinter as tk
import pygame
import pygame_menu
import time
import colors
import Connect4 as cFour
import Minimax as mx
def text_format(option, textSize, textColor):
"""
Creates a text object to show in the main menu
"""
newFont = pygame.font.Font(pygame_menu.font.FONT_FRANCHISE, textSize)
newText = newFont.render(option, 0, textColor)
return newText
def load_screen():
"""
This initializes the window for pygame to use
"""
screen = pygame.display.set_mode((600, 600))
pygame.display.set_caption("Connect4")
return screen
def get_player_details(screen):
"""
Creates a tkinter object(button) that gets players names
"""
root = tk.Tk()
root.title("Player Names!")
tk.Label(root, text="Player One", fg="blue").grid(row=0)
tk.Label(root, text="Player Two", fg="red").grid(row=1)
p1 = tk.Entry(root, font=(None, 15))
p2 = tk.Entry(root, font=(None, 15))
p1.grid(row=0, column=1)
p2.grid(row=1, column=1)
tk.Button(root, text='Play!', command= lambda: play_game(p1.get(),p2.get(), root, screen)).grid(row=10, column=1, sticky=tk.W)
tk.mainloop()
def get_player_ai_details(screen):
"""
Creating the panel to allow the user to select a color and go against the AI
"""
options = ["Player 1", "Player 2"]
root = tk.Tk()
root.title("Player 1(Blue) or 2(Red)?")
colorChoice= tk.StringVar(root)
colorChoice.set(options[0])
tk.OptionMenu(root, colorChoice, *options).grid(row=3)
p1 = tk.Entry(root, font=(None, 15))
p1.grid(row=3, column=1)
tk.Button(root, text="Play Computer!", command=lambda: play_computer(colorChoice.get(), p1.get(), root, screen)).grid(row=10, column=1)
tk.mainloop()
def play_computer(colorChoice, playerName, root, screen):
"""
Connect4 play function (human v computer)
"""
root.destroy()
if colorChoice == "Player 1":
mx.Minimax(Player(playerName), Player("Ed"), screen).play_computer()
else:
mx.Minimax(Player("Ed"), Player(playerName), screen).play_computer()
def play_game(p1Name, p2Name, root, screen):
"""
Connect4 play function (human v human)
"""
root.destroy()
game = cFour.Connect4(Player(p1Name.strip()), Player(p2Name.strip()), screen).play()
if __name__ == "__main__":
pygame.init()
screen = load_screen()
features = [
("Player Vs Player", colors.yellow),
("Player Vs AI", colors.red),
("Quit", colors.gray)
]
iterator = 0
menu = True
while menu:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
#This if block makes it where the user doesnt have to click arrow key up/down if they have exhausted the possible options, it will loop you throughout options
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_DOWN:
iterator += 1
if iterator == len(features):
iterator = 0
if event.key == pygame.K_UP:
iterator -= 1
if iterator < 0:
iterator = len(features) - 1
if event.key == pygame.K_RETURN:
if selected == "Player Vs Player":
get_player_details(screen)
if selected == "Player Vs AI":
get_player_ai_details(screen)
if selected == "Quit":
pygame.quit()
quit()
selected = features[iterator][0]
screen.fill(colors.blue)
screen_rect = screen.get_rect()
for i in range(0, len(features)):
counter = -50 + (i * 90) # Equation that sets distance between each choice in main menu
if i == iterator:
text = text_format(features[i][0], 80, features[i][1])
else:
text = text_format(features[i][0], 80, colors.black)
player_rect = text.get_rect(center=screen_rect.center)
player_rect[1] = player_rect[1] + counter
screen.blit(text, player_rect)
pygame.display.update()
Connect4.py
import pygame
import colors
import tkinter as tk
import pygame_menu
# import pandas as pd
import random
class Connect4:
"""
Class used to represent connect4 game
"""
def __init__(self, player1, player2, screen):
# Use 1 version of the screen instead of trying to create a new one
self.screen = screen
# Circle Radius and Width
self.WIDTH = 0
self.CIRCLERADIUS = 25
# Game-Time Variables
self.player1 = player1
self.player2 = player2
self.moveNumber = 0
self.gameOver = False
self.COLUMNS = 7
self.ROWS = 6
self.EMPTY = 99
self.board = [[self.EMPTY for x in range(self.COLUMNS)] for y in range(self.ROWS)]
# The distance between where the window starts and the game board is placed
self.DISTANCE = 90
# Space between each circle
self.DISTANCEGAP = 70
# Setting rectangle default
self.LEFT = 50
self.TOP = 70
self.HEIGHT = 470
self.RECWIDTH = 500
#Creating new tkinterobject
self.root = tk.Tk()
self.scoreboard = {self.player1.name: 0, self.player2.name: 0, "ties": 0}
# Storing locations of available moves given a user clicks the window -- Tuple of locations
self.POSITIONS = [
(
self.DISTANCE + (self.DISTANCEGAP*column) - self.CIRCLERADIUS,
self.DISTANCE + (self.DISTANCEGAP*column) + self.CIRCLERADIUS
)
for column in range(0, self.COLUMNS)
]
def who_won(self, board, piece):
"""
Determines the state of the game and finds if there is a winner
"""
# Horizontal
for col in range(0, self.COLUMNS - 3):
for row in range(0, self.ROWS):
if board[row][col] == piece and board[row][col + 1] == piece and board[row][col + 2] == piece and board[row][col + 3] == piece:
return True
# Vertical
for col in range(0, self.COLUMNS):
for row in range(0, self.ROWS - 3):
if board[row][col] == piece and board[row + 1][col] == piece and board[row + 2][col] == piece and board[row + 3][col] == piece:
return True
# Up-Left/Down-Right
for col in range(3, self.COLUMNS):
for row in range(3, self.ROWS):
if board[row][col] == piece and board[row - 1][col - 1] == piece and board[row - 2][col - 2] == piece and board[row - 3][col - 3] == piece:
return True
# Up-Right/Down-Left
for col in range(0, self.COLUMNS - 3):
for row in range(3, self.ROWS):
if board[row][col] == piece and board[row - 1][col + 1] == piece and board[row - 2][col + 2] == piece and board[row - 3][col + 3] == piece:
return True
# A winning move is not found
return False
def is_legal_move(self, position, board):
"""
Validates if a move is available/legal
"""
if board[0][position] == self.EMPTY:
return True
return False
def display_board(self):
"""
Displaying the game board to the user
"""
# Function: rect(surface, color, rectangle object, optional width) -- First one forms the outline of the board
pygame.draw.rect(self.screen, colors.salmon, (self.LEFT, self.TOP, self.RECWIDTH, self.HEIGHT), 13)
# This forms inner-most rectangle that users play on
pygame.draw.rect(self.screen, colors.burlywood, (self.LEFT, self.TOP, self.RECWIDTH, self.HEIGHT))
for column in range(0, self.COLUMNS):
colEq = self.DISTANCE + (self.DISTANCEGAP * column)
for row in range(0, self.ROWS):
# 125 is used here to make a the board placed in the center of the board and helps finding a value for self.TOP easier
rowEq = 125 + (self.DISTANCEGAP * row)
if self.board[row][column] == self.EMPTY:
color = colors.white
elif self.board[row][column] == 0:
color = colors.realBlue
elif self.board[row][column] == 1:
color = colors.red
pygame.draw.circle(self.screen, color, (colEq, rowEq), self.CIRCLERADIUS, self.WIDTH)
pygame.display.flip()
def play(self):
"""
This is the game-loop
"""
while not self.gameOver:
self.display_board()
if self.moveNumber % 2 == 0:
userText, userRect = self.display_player_name(self.player1.name, colors.realBlue)
elif self.moveNumber % 2 == 1:
userText, userRect = self.display_player_name(self.player2.name, colors.red)
self.screen.blit(userText, userRect)
for event in pygame.event.get():
self.screen.fill(colors.aquamarine) # Set up background color
if event.type == pygame.QUIT:
self.gameOver = True
elif event.type == pygame.MOUSEBUTTONDOWN:
x, y = pygame.mouse.get_pos()
position = self.get_column_position(x)
if self.moveNumber % 2 == 0 and position != self.EMPTY:
if self.is_legal_move(position, self.board):
self.drop_piece_animation(position)
if self.who_won(self.board, 0):
self.gameOver = True
self.scoreboard[self.player1.name] = self.scoreboard.get(self.player1.name) + 1
userText, userRect = self.display_player_name(self.player1.name + " " + "Wins!!!", colors.dark_gray)
elif self.check_if_tie(self.board):
self.gameOver = True
self.scoreboard["ties"] = self.scoreboard.get("ties") + 1
userText, userRect = self.display_player_name("It is a TIE!!!", colors.dark_gray)
elif self.moveNumber % 2 == 1 and position != self.EMPTY:
if self.is_legal_move(position, self.board):
self.drop_piece_animation(position)
if self.who_won(self.board, 1):
self.gameOver = True
self.scoreboard[self.player2.name] = self.scoreboard.get(self.player2.name) + 1
userText, userRect = self.display_player_name(self.player2.name + " " + "Wins!!!", colors.dark_gray)
elif self.check_if_tie(self.board):
self.gameOver = True
self.scoreboard["ties"] = self.scoreboard.get("ties") + 1
userText, userRect = self.display_player_name("It is a TIE!!!", colors.dark_gray)
self.display_board()
self.screen.blit(userText, userRect)
pygame.display.flip()
self.display_scoreboard(False)
def display_scoreboard(self, isAi):
"""
This enables the tkinter object so I can display the user options after : Victory/Loss/Tie
"""
self.root.geometry('460x150+300+0')
self.reset()
self.root.title("Choices")
# This creates the feedback information screen that the user sees after a game
tk.Label(self.root, text="Close window to go to main menu", font=(None, 15, 'underline'), anchor='w', justify='left').grid(row=0, column=1, sticky="NSEW")
tk.Label(self.root, text=self.player1.name + ": " + str(self.scoreboard.get(self.player1.name)), font=(None, 15), anchor='w', justify='left').grid(row=1, column=1, sticky = "NSEW")
tk.Label(self.root, text=self.player2.name + ": " + str(self.scoreboard.get(self.player2.name)), font=(None, 15), anchor='w', justify='left').grid(row=2, column=1, sticky="NSEW")
tk.Label(self.root, text="Ties: " + str(self.scoreboard.get("ties")), font=(None, 15), anchor='w', justify='left').grid(row=3, column=1, sticky="NSEW")
# if isAi == True:
# # tk.Button(self.root, text='Rematch!', command=self.playAi, font=(None, 12), fg="blue").grid(row=4, column=1, sticky=tk.W)
# else:
tk.Button(self.root, text='Rematch!', command=self.play, font=(None, 12), fg="blue").grid(row=4, column=1, sticky=tk.W)
# tk.Button(self.root, text='Rematch with Swap!', command= lambda: self.swapPlayers(isAi), font=(None, 12), fg="red").grid(row=4, column=2, sticky=tk.W)
tk.Entry(self.root)
self.root.mainloop()
def check_if_tie(self, board):
"""
A possible game state : Checking for a tie
"""
totalPieces = 0
for col in range(0, self.COLUMNS):
for row in range(0, self.ROWS):
if board[row][col] == 0 or board[row][col] == 1:
totalPieces += 1
if totalPieces == 42:
return True
else:
return False
def display_player_name(self, name, color):
"""
A feature to help users know who's turn it is that gets displayed
"""
font = pygame.font.Font(pygame_menu.font.FONT_FRANCHISE, 60)
text = font.render(name, True, color)
textRect = text.get_rect()
textRect.center = (len(name) * 30, 20)
return text, textRect
def drop_piece_animation(self, position):
"""
Inserting a piece at a given position with the animation of a piece drop
"""
tmpRow = 5
while self.board[tmpRow][position] == 1 or self.board[tmpRow][position] == 0:
tmpRow -= 1
for i in range(0, tmpRow + 1):
self.board[i][position] = self.moveNumber % 2
self.display_board()
pygame.time.delay(200)
pygame.display.flip()
self.board[i][position] = self.EMPTY
self.board[tmpRow][position] = self.moveNumber % 2
self.moveNumber += 1
def get_column_position(self, position):
"""
Takes a X coordinate value dependent on a click and determines what column user clicked
"""
index = 0
for i in self.POSITIONS:
if position + self.CIRCLERADIUS/2 >= i[0] and position - self.CIRCLERADIUS/2 <= i[1]:
return index
index += 1
return self.EMPTY
def reset(self):
"""
Restoring the game in its original state
"""
self.moveNumber = 0
self.board = [[self.EMPTY for x in range(self.COLUMNS)] for y in range(self.ROWS)]
self.gameOver = False
def play_computer(self):
"""
This is the game-loop used for AI play
"""
# If/else block to distinguish the human/Ai because the ai cant mouse click events
if self.player1.name == "Ed": # Ed Watkins (Staten Island)
humanPlayer = 1
computerPlayer = 0
humanName = self.player2.name
computerName = self.player1.name
elif self.player2.name == "Ed":
humanPlayer = 0
computerPlayer = 1
humanName = self.player1.name
computerName = self.player2.name
while not self.gameOver:
self.display_board()
if self.moveNumber % 2 == 0:
userText, userRect = self.display_player_name(self.player1.name, colors.blue)
elif self.moveNumber % 2 == 1:
userText, userRect = self.display_player_name(self.player2.name, colors.red)
self.screen.blit(userText, userRect)
for event in pygame.event.get():
self.screen.fill(colors.aquamarine) # Set up background color
if event.type == pygame.QUIT:
self.gameOver = True
elif event.type == pygame.MOUSEBUTTONDOWN:
x, y = pygame.mouse.get_pos()
position = self.get_column_position(x)
if self.moveNumber % 2 == humanPlayer and position != self.EMPTY:
if self.is_legal_move(position, self.board):
self.drop_piece_animation(position)
if self.who_won(self.board, humanPlayer):
self.gameOver = True
self.scoreboard[humanName] = self.scoreboard.get(humanName) + 1
userText, userRect = self.display_player_name(humanName + " " + "Wins!!!", colors.dark_gray)
elif self.check_if_tie(self.board):
self.gameOver = True
self.scoreboard["ties"] = self.scoreboard.get("ties") + 1
userText, userRect = self.display_player_name("It is a TIE!!!", colors.dark_gray)
if self.moveNumber % 2 == computerPlayer and self.gameOver == False:
move = self.generate_move(self.board, 4, computerPlayer, humanPlayer, True, self.moveNumber)
self.drop_piece_animation(move)
if self.who_won(self.board, computerPlayer):
self.gameOver = True
self.scoreboard[computerName] = self.scoreboard.get(computerName) + 1
userText, userRect = self.display_player_name(computerName + " " + "Wins!!!", colors.dark_gray)
elif self.check_if_tie(self.board):
self.gameOver = True
self.scoreboard["ties"] = self.scoreboard.get("ties") + 1
userText, userRect = self.display_player_name("It is a TIE!!!", colors.dark_gray)
self.display_board()
self.screen.blit(userText, userRect)
pygame.display.flip()
Minimax.py
from Connect4 import Connect4
import random
from copy import copy, deepcopy
import pygame
class Minimax(Connect4):
def __init__(self, player1, player2, screen):
super().__init__(player1, player2, screen)
def is_game_over(self, board):
if self.who_won(board, 1) or self.who_won(board, 0):
return True
return False
def generate_move(self, board, depth, computerPlayer, humanPlayer, maximizingPlayer, moveNumber):
if depth == 0 or self.is_game_over(board) or self.check_if_tie(board):
if self.is_game_over(board):
if self.who_won(board, computerPlayer):
return 1000000
elif self.who_won(board, humanPlayer):
return -1000000
elif self.check_if_tie(board):
return 0
else:
return self.get_game_score(board, computerPlayer, humanPlayer)
if maximizingPlayer:
maxValue = -1000000
for move in range(0, self.COLUMNS):
tmpBoard = self.copyBoard(board)
if self.is_legal_move(move, tmpBoard):
self.drop_piece_computer(move, tmpBoard, moveNumber)
result = self.generate_move(tmpBoard, depth - 1, computerPlayer, humanPlayer, False, moveNumber + 1)
if result >= maxValue:
maxValue = result
bestMove = move
return bestMove
else:
minValue = 1000000
for move in range(0,self.COLUMNS):
tmpBoard = self.copyBoard(board)
if self.is_legal_move(move, tmpBoard):
self.drop_piece_computer(move, tmpBoard, moveNumber)
result = self.generate_move(tmpBoard, depth - 1, humanPlayer, humanPlayer, True, moveNumber + 1)
if result <= minValue:
minValue = result
thismove = move
return thismove
def copyBoard(self, board):
tmpList = [[self.EMPTY for x in range(self.COLUMNS)] for y in range(self.ROWS)]
for row in range(0, self.ROWS):
for col in range(0, self.COLUMNS):
tmpList[row][col] = board[row][col]
return tmpList
def drop_piece_computer(self, position, board, moveNumber):
"""
Inserting a piece at a given position with the animation of a piece drop
"""
tmpRow = 5
while board[tmpRow][position] == 1 or board[tmpRow][position] == 0:
tmpRow -= 1
board[tmpRow][position] = moveNumber % 2
# moveNumber += 1
def get_game_score(self, board, computerPlayer, humanPlayer):
totalScore = 0
totalScore += self.get_hori_score(board, computerPlayer, humanPlayer)
# totalScore += self.get_vert_score(board, computerPlayer, humanPlayer)
# totalScore += self.get_upright_score(board, computerPlayer, humanPlayer)
# totalScore += self.get_upleft_score(board, computerPlayer, humanPlayer)
return totalScore
def get_hori_score(self, board, computerPlayer, humanPlayer):
score = 0
# List to collect all the groupings of 4(Horizontally) out of the current game state
groupingFourList = []
for col in range(0, self.COLUMNS - 3):
for row in range(0, self.ROWS):
groupingFourList.append(board[row][col])
groupingFourList.append(board[row][col + 1])
groupingFourList.append(board[row][col + 2])
groupingFourList.append(board[row][col + 3])
computerPieces = self.count_player_pieces(groupingFourList, 1)
humanPieces = self.count_player_pieces(groupingFourList, 0)
emptyPieces = self.count_player_pieces(groupingFourList, self.EMPTY)
score += self.score_metric(computerPieces, humanPieces, emptyPieces)
groupingFourList = []
return score
def get_upright_score(self, board, computerPlayer, humanPlayer):
score = 0
# List to collect all the groupings of 4(Horizontally) out of the current game state
groupingFourList = []
for col in range(0, self.COLUMNS - 3):
for row in range(3, self.ROWS):
groupingFourList.append(board[row][col])
groupingFourList.append(board[row - 1][col + 1])
groupingFourList.append(board[row - 2][col + 2])
groupingFourList.append(board[row - 3][col + 3])
computerPieces = self.count_player_pieces(groupingFourList, 1)
humanPieces = self.count_player_pieces(groupingFourList, 0)
emptyPieces = self.count_player_pieces(groupingFourList, self.EMPTY)
score += self.score_metric(computerPieces, humanPieces, emptyPieces)
groupingFourList = []
return score
def get_upleft_score(self, board, computerPlayer, humanPlayer):
score = 0
# List to collect all the groupings of 4(Horizontally) out of the current game state
groupingFourList = []
for col in range(3, self.COLUMNS):
for row in range(3, self.ROWS):
groupingFourList.append(board[row][col])
groupingFourList.append(board[row - 1][col - 1])
groupingFourList.append(board[row - 2][col - 2])
groupingFourList.append(board[row - 3][col - 3])
computerPieces = self.count_player_pieces(groupingFourList, 1)
humanPieces = self.count_player_pieces(groupingFourList, humanPlayer)
emptyPieces = self.count_player_pieces(groupingFourList, self.EMPTY)
score += self.score_metric(computerPieces, humanPieces, emptyPieces)
groupingFourList = []
return score
def get_vert_score(self, board, computerPlayer, humanPlayer):
score = 0
# List to collect all the groupings of 4(Horizontally) out of the current game state
groupingFourList = []
for col in range(0, self.COLUMNS):
for row in range(0, self.ROWS -3):
groupingFourList.append(board[row][col])
groupingFourList.append(board[row + 1][col])
groupingFourList.append(board[row + 2][col])
groupingFourList.append(board[row + 3][col])
computerPieces = self.count_player_pieces(groupingFourList, computerPlayer)
humanPieces = self.count_player_pieces(groupingFourList, humanPlayer)
emptyPieces = self.count_player_pieces(groupingFourList, self.EMPTY)
score += self.score_metric(computerPieces, humanPieces, emptyPieces)
groupingFourList = []
return score
def count_player_pieces(self, groupingFourList, playerPiece):
totalPieces = 0
for piece in groupingFourList:
if piece == playerPiece:
totalPieces += 1
return totalPieces
def score_metric(self, computerPieces, humanPieces, emptyPieces):
score = 0
# Making bot prioritize playing defense than offense
# Thats why the score is lower when regarding the enemy: AI chooses highest scoring move
if (computerPieces == 4):
score += 100
elif (computerPieces == 3 and emptyPieces == 1):
score += 20
elif (computerPieces == 2 and emptyPieces == 2):
score += 10
if (humanPieces == 3 and emptyPieces == 1):
score -= 100
return score
colors.py
"""
Valid colors to use got it from this link : https://python-forum.io/Thread-PyGame-PyGame-Colors
"""
realBlue = (0,0,255)
white = (255,255,255)
green = (0,255,0)
black = (0,0,0)
orange = (255,100,10)
blue_green = (0,255,170)
marroon = (115,0,0)
lime = (180,255,100)
pink = (255,100,180)
purple = (240,0,255)
magenta = (255,0,230)
brown = (100,40,0)
forest_green = (0,50,0)
navy_blue = (0,0,100)
rust = (210,150,75)
dandilion_yellow = (255,200,0)
highlighter = (255,255,100)
sky_blue = (0,255,255)
light_gray = (200,200,200)
dark_gray = (50,50,50)
tan = (230,220,170)
coffee_brown = (200,190,140)
moon_glow = (235, 245, 255)
burlywood = (255, 211, 155)
salmon = (139, 76, 57)
aquamarine = (127, 255, 212)
#Colors used for menu
blue = (135, 206, 250)
yellow = (255, 255, 0)
red = (255,0,0)
gray = (128, 128, 128)
Player.py
class Player():
def __init__(self, name):
self.name = name
The solution is solved but stackoverflow wont allow me to remove the question. The reason I would like the question removed because the answer provided isnt the solution so it would just throw other people off.
Ok the solution is as follows:
In this section of code this is going through a portion of the search space and evaluating the game states based on the best moves for each player. Through this algorithm the AI knows the best moves for each player and can make a "good" move.
if maximizingPlayer:
maxValue = -1000000
for move in range(0, self.COLUMNS):
tmpBoard = self.copyBoard(board)
if self.is_legal_move(move, tmpBoard):
self.drop_piece_computer(move, tmpBoard, moveNumber)
result = self.generate_move(tmpBoard, depth - 1, computerPlayer, humanPlayer, False, moveNumber + 1)
if result >= maxValue:
maxValue = result
bestMove = move
return bestMove
else:
minValue = 1000000
for move in range(0,self.COLUMNS):
tmpBoard = self.copyBoard(board)
if self.is_legal_move(move, tmpBoard):
self.drop_piece_computer(move, tmpBoard, moveNumber)
result = self.generate_move(tmpBoard, depth - 1, humanPlayer, humanPlayer, True, moveNumber + 1)
if result <= minValue:
minValue = result
thismove = move
return thismove
However, if you look closely when I recursively call the function back to the AI in the !maximizing player function I have:
result = self.generate_move(tmpBoard, depth - 1, humanPlayer, humanPlayer, True, moveNumber + 1)
In words when a simulated human player was playing on a generic board to generate the ai move it was assuming that there were 2 human players, and thats why the halt would happen because you cannot have a game with 2 of the same player.
so changing:
result = self.generate_move(tmpBoard, depth - 1, humanPlayer, humanPlayer, True, moveNumber + 1)
to this:
result = self.generate_move(tmpBoard, depth - 1, computerPlayer, humanPlayer, True, moveNumber + 1)
Putting False instead of True in the if maximizingPlayer block and True instead of False in the else clause for the second to last argument to the recursive calls to generate_move seems to fix the game.
i.e. Change False on line 33 of Minimax.py to True and change the True on line 44 to False.

Object follows coordinates (pygame)

I have an object that needs to follow 4 points given by the coordinates in self.list. When I execute the code, I can see that the checks are passed and it augments the search coordinates to the next point but the object on screen only goes to the last one.
Working in Python 3 and in pygame.
How can I fix this?
class Enemy:
def __init__(self, image):
super().__init__()
self.image = pygame.image.load(image)
self.rect = self.image.get_rect()
self.rect.x = 1280
self.rect.y = randrange(abs(720 - self.rect.height))
self.pattern = 2
self.list = [(1100,360),(900,180),(700,360),(900,540)]
self.tuple_dest = self.list[0]
self.i = 0
self.p=False
def move(self, player):
if self.tuple_dest[0] <= self.rect.x:
self.rect.x -= 1
elif self.tuple_dest[0] >= self.rect.x:
self.rect.x += 1
if self.tuple_dest[1] <= self.rect.y:
self.rect.y -= 1
elif self.tuple_dest[1] >= self.rect.y:
self.rect.y += 1
#check if arrived
print(self.p)
if self.tuple_dest[0] == self.list[self.i][0] and self.tuple_dest[1] == self.list[self.i][1] and self.p == False:
self.p = True
if self.i < (len(self.list)-1) and self.p==True:
print(self.p)
self.i += 1
print(self.i)
self.tuple_dest = self.list[self.i]
self.p = False
This is the problematic line:
if self.tuple_dest[0] == self.list[self.i][0] and self.tuple_dest[1] == self.list[self.i][1] and self.p == False:
You're just checking if the coordinates in the tuple are equal to the same tuple in the self.list, so the condition is immediately True.
You need to check if the current target coordinates are equal to the self.rect coordinates:
tx, ty = self.tuple_dest
if tx == self.rect.x and ty == self.rect.y and not self.p:
self.p = True

Resources