12 files changed, 194 insertions(+), 184 deletions(-)
D collatz-gui/__init__.py
D collatz-gui/app.py
A collatz/__init__.py
A collatz/app.py
R collatz-gui/dialog.py => collatz/dialog.py
R collatz-gui/icon.svg => collatz/icon.svg
R collatz-gui/main.py => collatz/main.py
R collatz-gui/validator.py => collatz/validator.py
D tic-tac-toe/main.py
R tic-tac-toe/__init__.py => tictactoe/__init__.py
R tic-tac-toe/tictactoe.py => tictactoe/game.py
A tictactoe/main.py
D collatz-gui/__init__.py => collatz-gui/__init__.py +0 -2
@@ 1,2 0,0 @@
-""" This is a samll GUI for checking the Collatz conjecture on your favourite natural numbers and seeing the steps performed to reach 1. It utilizes the Qt5 framework.
-"""
D collatz-gui/app.py => collatz-gui/app.py +0 -132
@@ 1,132 0,0 @@
-import os
-import webbrowser
-from PyQt5 import QtCore, QtGui, QtWidgets
-from validator import IntValidator
-from dialog import BrowserDialog
-
-
-class CollatzApp(QtWidgets.QMainWindow):
- """Main app window."""
-
- def __init__(self):
- super().__init__()
- self.setWindowTitle(' Collatz')
- self.setFixedSize(550, 700)
- scriptDir = os.path.dirname(os.path.realpath(__file__))
- self.setWindowIcon(QtGui.QIcon(scriptDir + os.path.sep + 'icon.svg'))
-
- # Create start button
- self.startButton = QtWidgets.QPushButton('Start', self)
- self.startButton.clicked.connect(self._on_click_start)
-
- # Create textbox for number input
- self.inputBox = QtWidgets.QLineEdit(self)
- self.inputBox.setValidator(IntValidator(bottom=1))
- # Enter to press 'Start' button
- self.inputBox.returnPressed.connect(self.startButton.click)
- # Set textbox font
- self.inputBox.font()
- f = self.inputBox.font()
- f.setPointSize(14)
- self.inputBox.setFont(f)
-
- # Create 'Steps' label
- self.stepsLabel = QtWidgets.QLabel(self)
- self.stepsLabel.setText('Steps')
- self.stepsLabel.setAlignment(QtCore.Qt.AlignCenter)
- f = self.stepsLabel.font()
- f.setPointSize(10)
- self.stepsLabel.setFont(f)
-
- # Create textarea for iterations display
- self.stepsDisplay = QtWidgets.QPlainTextEdit(self)
- f = self.stepsDisplay.font()
- f.setPointSize(12)
- self.stepsDisplay.setFont(f)
- # Set tab width to 4 spaces
- self.stepsDisplay.setTabStopDistance(
- QtGui.QFontMetricsF(f).horizontalAdvance(' ') * 4)
- self.stepsDisplay.setLineWrapMode(QtWidgets.QPlainTextEdit.NoWrap)
- self.stepsDisplay.setReadOnly(True)
-
- # Create clear button
- self.clearButton = QtWidgets.QPushButton('Clear', self)
- self.clearButton.clicked.connect(self._on_click_clear)
-
- # Create info button
- self.infoButton = QtWidgets.QPushButton('Info', self)
- self.infoButton.clicked.connect(self._on_click_info)
-
- # Add widgets to window
- hBoxTop = QtWidgets.QHBoxLayout()
- hBoxTop.addWidget(self.inputBox)
- hBoxTop.addWidget(self.startButton)
- vBox = QtWidgets.QVBoxLayout()
- vBox.addLayout(hBoxTop)
- vBox.addWidget(self.stepsLabel)
- vBox.addWidget(self.stepsDisplay)
- hBoxBottom = QtWidgets.QHBoxLayout()
- hBoxBottom.addStretch()
- hBoxBottom.addWidget(self.clearButton)
- hBoxBottom.addWidget(self.infoButton)
- vBox.addLayout(hBoxBottom)
- # Central widget
- central = QtWidgets.QWidget(self)
- central.setLayout(vBox)
- self.setCentralWidget(central)
- self.show()
- self.inputBox.setFocus()
-
- def _sequence(self, number):
- """Perform a recursive check with the Collatz sequence and print each step to `stepsDisplay`.
-
- Raises `Exception` and terminates if the `number` is 1.
- """
-
- self.stepsDisplay.insertPlainText(str(number))
- if number == 1:
- self.stepsDisplay.insertPlainText('\n')
- raise Exception()
- else:
- self.counter += 1
- if number % 2 == 0:
- self.stepsDisplay.insertPlainText('\t| /2\n')
- self._sequence(int(number / 2))
- else:
- self.stepsDisplay.insertPlainText('\t| *3 + 1\n')
- self._sequence(int(3 * number + 1))
-
- @ QtCore.pyqtSlot()
- def _on_click_start(self):
- """Clear the `stepsDisplay` and start the Collatz sequence."""
-
- self.stepsDisplay.clear()
- try:
- textboxValue = int(self.inputBox.text())
- except Exception:
- # Empty input - ignore
- return
- try:
- self.counter = 0
- self._sequence(textboxValue)
- except Exception:
- self.stepsDisplay.insertPlainText(
- f'Terminated after {self.counter} iterations.')
- self.stepsDisplay.moveCursor(QtGui.QTextCursor.End)
-
- @ QtCore.pyqtSlot()
- def _on_click_clear(self):
- """Clear the `inputBox` and `stepsDisplay`."""
-
- self.inputBox.clear()
- self.stepsDisplay.clear()
- self.inputBox.setFocus()
-
- @ QtCore.pyqtSlot()
- def _on_click_info(self):
- """Open a dialog window to acces the relevant Wikipedia article."""
-
- if BrowserDialog(self).exec():
- webbrowser.open('https://en.wikipedia.org/wiki/Collatz_conjecture')
- else:
- self.inputBox.setFocus()
A collatz/__init__.py => collatz/__init__.py +4 -0
@@ 0,0 1,4 @@
+""" This is a samll GUI for checking the Collatz conjecture on your
+favourite natural numbers and seeing the steps performed to reach 1.
+It utilizes the Qt5 framework.
+"""
A collatz/app.py => collatz/app.py +132 -0
@@ 0,0 1,132 @@
+"""This module contains the main app window"""
+import os
+import webbrowser
+from PyQt5 import QtCore, QtGui, QtWidgets
+from validator import IntValidator
+from dialog import BrowserDialog
+
+
+class CollatzApp(QtWidgets.QMainWindow):
+ """Main app window."""
+
+ def __init__(self):
+ super().__init__()
+ self.setWindowTitle(' Collatz')
+ self.setFixedSize(550, 700)
+ dir_name = os.path.dirname(os.path.realpath(__file__))
+ self.setWindowIcon(QtGui.QIcon(dir_name + os.path.sep + 'icon.svg'))
+ self.counter = 0
+
+ # Create start button
+ self.start_button = QtWidgets.QPushButton('Start', self)
+ self.start_button.clicked.connect(self._on_click_start)
+
+ # Create textbox for number input
+ self.input_box = QtWidgets.QLineEdit(self)
+ self.input_box.setValidator(IntValidator(bottom=1))
+ # Enter to press 'Start' button
+ self.input_box.returnPressed.connect(self.start_button.click)
+ # Set textbox font
+ self.input_box.font()
+ font_ = self.input_box.font()
+ font_.setPointSize(14)
+ self.input_box.setFont(font_)
+
+ # Create 'Steps' label
+ self.steps_label = QtWidgets.QLabel(self)
+ self.steps_label.setText('Steps')
+ self.steps_label.setAlignment(QtCore.Qt.AlignCenter)
+ font_ = self.steps_label.font()
+ font_.setPointSize(10)
+ self.steps_label.setFont(font_)
+
+ # Create textarea for iterations display
+ self.steps_display = QtWidgets.QPlainTextEdit(self)
+ font_ = self.steps_display.font()
+ font_.setPointSize(12)
+ self.steps_display.setFont(font_)
+ # Set tab width to 4 spaces
+ self.steps_display.setTabStopDistance(
+ QtGui.QFontMetricsF(font_).horizontalAdvance(' ') * 4)
+ self.steps_display.setLineWrapMode(QtWidgets.QPlainTextEdit.NoWrap)
+ self.steps_display.setReadOnly(True)
+
+ # Create clear button
+ self.clear_button = QtWidgets.QPushButton('Clear', self)
+ self.clear_button.clicked.connect(self._on_click_clear)
+
+ # Create info button
+ self.info_button = QtWidgets.QPushButton('Info', self)
+ self.info_button.clicked.connect(self._on_click_info)
+
+ # Add widgets to window
+ h_box_top = QtWidgets.QHBoxLayout()
+ h_box_top.addWidget(self.input_box)
+ h_box_top.addWidget(self.start_button)
+ v_box = QtWidgets.QVBoxLayout()
+ v_box.addLayout(h_box_top)
+ v_box.addWidget(self.steps_label)
+ v_box.addWidget(self.steps_display)
+ h_box_bottom = QtWidgets.QHBoxLayout()
+ h_box_bottom.addStretch()
+ h_box_bottom.addWidget(self.clear_button)
+ h_box_bottom.addWidget(self.info_button)
+ v_box.addLayout(h_box_bottom)
+ # Central widget
+ central = QtWidgets.QWidget(self)
+ central.setLayout(v_box)
+ self.setCentralWidget(central)
+ self.show()
+ self.input_box.setFocus()
+
+ def _sequence(self, number):
+ """Perform a recursive check with the Collatz sequence
+ and print each step to `stepsDisplay`.
+
+ Raises `Exception` and terminates if the `number` is 1.
+ """
+
+ self.steps_display.insertPlainText(str(number))
+ if number == 1:
+ self.steps_display.insertPlainText('\n')
+ else:
+ self.counter += 1
+ if number % 2 == 0:
+ self.steps_display.insertPlainText('\t| /2\n')
+ self._sequence(int(number / 2))
+ else:
+ self.steps_display.insertPlainText('\t| *3 + 1\n')
+ self._sequence(int(3 * number + 1))
+
+ @ QtCore.pyqtSlot()
+ def _on_click_start(self):
+ """Clear the `stepsDisplay` and start the Collatz sequence."""
+
+ self.steps_display.clear()
+ try:
+ input_value = int(self.input_box.text())
+ except ValueError:
+ # Empty input - ignore
+ return
+ self.counter = 0
+ self._sequence(input_value)
+ self.steps_display.insertPlainText(
+ f'Terminated after {self.counter} iterations.')
+ self.steps_display.moveCursor(QtGui.QTextCursor.End)
+
+ @ QtCore.pyqtSlot()
+ def _on_click_clear(self):
+ """Clear the `inputBox` and `stepsDisplay`."""
+
+ self.input_box.clear()
+ self.steps_display.clear()
+ self.input_box.setFocus()
+
+ @ QtCore.pyqtSlot()
+ def _on_click_info(self):
+ """Open a dialog window to acces the relevant Wikipedia article."""
+
+ if BrowserDialog(self).exec():
+ webbrowser.open('https://en.wikipedia.org/wiki/Collatz_conjecture')
+ else:
+ self.input_box.setFocus()
R collatz-gui/dialog.py => collatz/dialog.py +8 -6
@@ 1,9 1,11 @@
+"""This module contains the custom dialog box."""
import os
from PyQt5 import QtGui, QtWidgets, QtCore
class BrowserDialog(QtWidgets.QDialog):
- """ This is a custom dialog box for opening the Collatz conjecture Wikipedia page in a web browser.
+ """ This is a custom dialog box for opening the Collatz
+ conjecture Wikipedia page in a web browser.
"""
def __init__(self, *args, **kwargs):
@@ 14,10 16,10 @@ class BrowserDialog(QtWidgets.QDialog):
os.path.dirname(__file__) + '/icon.svg'))
# Buttons
- QBtn = QtWidgets.QDialogButtonBox.Ok | QtWidgets.QDialogButtonBox.Cancel
- self.buttonBox = QtWidgets.QDialogButtonBox(QBtn)
- self.buttonBox.accepted.connect(self.accept)
- self.buttonBox.rejected.connect(self.reject)
+ q_btn = QtWidgets.QDialogButtonBox.Ok | QtWidgets.QDialogButtonBox.Cancel
+ self.button_box = QtWidgets.QDialogButtonBox(q_btn)
+ self.button_box.accepted.connect(self.accept)
+ self.button_box.rejected.connect(self.reject)
self.message = QtWidgets.QLabel(
'This will open your browser.\nContinue?', self)
@@ 25,5 27,5 @@ class BrowserDialog(QtWidgets.QDialog):
self.layout = QtWidgets.QVBoxLayout()
self.layout.addWidget(self.message)
- self.layout.addWidget(self.buttonBox)
+ self.layout.addWidget(self.button_box)
self.setLayout(self.layout)
R collatz-gui/icon.svg => collatz/icon.svg +0 -0
R collatz-gui/main.py => collatz/main.py +4 -3
@@ 1,11 1,12 @@
+"""This is the __main__ module."""
import sys
import os
-from app import CollatzApp
from PyQt5 import QtWidgets, QtGui
+from app import CollatzApp
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
- scriptDir = os.path.dirname(os.path.realpath(__file__))
- app.setWindowIcon(QtGui.QIcon(scriptDir + os.path.sep + 'icon.svg'))
+ dir_name = os.path.dirname(os.path.realpath(__file__))
+ app.setWindowIcon(QtGui.QIcon(dir_name + os.path.sep + 'icon.svg'))
ex = CollatzApp()
app.exec()
R collatz-gui/validator.py => collatz/validator.py +6 -3
@@ 1,9 1,11 @@
-import sys
-from PyQt5 import QtCore, QtGui, QtWidgets
+"""This module contains the arbitraryly big int validator."""
+from PyQt5 import QtGui
class IntValidator(QtGui.QDoubleValidator):
- """Normal QIntValidator is limited to 32-bit signed size. This version utilizes QDoubleValidator to allow for arbitraryly big integers, which are possible in Python.
+ """Normal QIntValidator is limited to 32-bit signed size.
+ This version utilizes QDoubleValidator to allow for arbitraryly big
+ integers, which are possible in Python.
"""
def __init__(self, bottom=float('-inf'), top=float('inf')):
@@ 11,6 13,7 @@ class IntValidator(QtGui.QDoubleValidator):
self.setNotation(QtGui.QDoubleValidator.StandardNotation)
def validate(self, text, pos):
+ """Validate which disalows the '.' symbol."""
# Disallow '.' symbol since only ints are allowed
if text.endswith('.'):
return QtGui.QValidator.Invalid, text, pos
D tic-tac-toe/main.py => tic-tac-toe/main.py +0 -13
@@ 1,13 0,0 @@
-from tictactoe import print_board, input_position, check_win
-import random
-
-if __name__ == "__main__":
- current_player = random.choice(['o', 'x'])
- while True:
- print_board()
- check_win()
- input_position(current_player)
- if current_player == 'x':
- current_player = 'o'
- else:
- current_player = 'x'
R tic-tac-toe/__init__.py => tictactoe/__init__.py +0 -0
R tic-tac-toe/tictactoe.py => tictactoe/game.py +26 -25
@@ 1,21 1,23 @@
+"""This module contains the game rules."""
import os
+import sys
# Global variable board is a 0-9 list, which eventually get replaced with x/o
-board = list(range(9))
+BOARD = list(range(9))
def print_board():
"""Clear the terminal screen and print the board."""
- global board
+ global BOARD
# Clear the screen with cls (Windows) or clear (other systems)
os.system('cls' if os.name == 'nt' else 'clear')
# Print the board
- print(f'{board[0]} | {board[1]} | {board[2]}')
+ print(f'{BOARD[0]} | {BOARD[1]} | {BOARD[2]}')
print('----------')
- print(f'{board[3]} | {board[4]} | {board[5]}')
+ print(f'{BOARD[3]} | {BOARD[4]} | {BOARD[5]}')
print('----------')
- print(f'{board[6]} | {board[7]} | {board[8]}', end='\n\n')
+ print(f'{BOARD[6]} | {BOARD[7]} | {BOARD[8]}', end='\n\n')
def input_position(player):
@@ 25,13 27,12 @@ def input_position(player):
try:
pos_string = input(f'{player}: ')
if pos_string == 'q':
- exit()
+ sys.exit()
position = int(pos_string)
- if position in board:
- board[position] = player
+ if position in BOARD:
+ BOARD[position] = player
break
- else:
- raise ValueError
+ raise ValueError
except ValueError:
print('Invalid position, try again or "q" to quit.')
@@ 39,32 40,32 @@ def input_position(player):
def check_win():
"""Check if there is a winner (or draw) and end the game if there is."""
- global board
+ global BOARD
h_index = 0
v_index = 0
# Check horizontal lines with h_index and vertical lines with v_index
for _ in range(0, 3):
- if board[h_index] == board[h_index + 1] == board[h_index + 2]:
- print(f'{board[h_index]} won!')
- exit()
- if board[v_index] == board[v_index + 3] == board[v_index + 6]:
- print(f'{board[v_index]} won!')
- exit()
+ if BOARD[h_index] == BOARD[h_index + 1] == BOARD[h_index + 2]:
+ print(f'{BOARD[h_index]} won!')
+ sys.exit()
+ if BOARD[v_index] == BOARD[v_index + 3] == BOARD[v_index + 6]:
+ print(f'{BOARD[v_index]} won!')
+ sys.exit()
h_index += 3
v_index += 1
# Check diagonals
- if board[0] == board[4] == board[8]:
- print(f'{board[0]} won!')
- exit()
- if board[2] == board[4] == board[6]:
- print(f'{board[2]} won!')
- exit()
+ if BOARD[0] == BOARD[4] == BOARD[8]:
+ print(f'{BOARD[0]} won!')
+ sys.exit()
+ if BOARD[2] == BOARD[4] == BOARD[6]:
+ print(f'{BOARD[2]} won!')
+ sys.exit()
# Check if the board is NOT full
- for cell in board:
+ for cell in BOARD:
if str(cell) in '012345678':
break
# If full draw the game
else:
print('Draw!')
- exit()
+ sys.exit()
A tictactoe/main.py => tictactoe/main.py +14 -0
@@ 0,0 1,14 @@
+"""This is the __main__ module."""
+import random
+from game import print_board, input_position, check_win
+
+if __name__ == "__main__":
+ CURRENT_PLAYER = random.choice(['o', 'x'])
+ while True:
+ print_board()
+ check_win()
+ input_position(CURRENT_PLAYER)
+ if CURRENT_PLAYER == 'x':
+ CURRENT_PLAYER = 'o'
+ else:
+ CURRENT_PLAYER = 'x'