Skip to content
Snippets Groups Projects
Commit 413d5d21 authored by Niloofar's avatar Niloofar
Browse files

Initial commit

parents
Branches main
No related tags found
No related merge requests found
Showing
with 427 additions and 0 deletions
.DS_Store 0 → 100644
File added
# Software Maintenance Example
This Python package implements a command-line program that plays tic-tac-toe,
but contains a bug that causes the computer to draw a line that it should win.
It is used in SOFT 162 to introduce the software engineering terminology for
activities, artifacts, tools, quality dimensions, and metrics in context and
with concrete examples.
## Testing
The unit test suite can be run by entering the command
```sh
python3 -m unittest
```
from the repository's root directory.
## Running
The program can be run uninstalled by entering the command
```sh
python3 -m tic_tac_toe.tic_tac_toe
```
from the repository's root directory.
## Packaging
The program can be packaged for installation with `pip` by entering the command
```sh
python3 ./setup.py sdist
```
from the repository's root directory.
File added
setup.py 0 → 100644
import setuptools
setuptools.setup(
name='tic_tac_toe',
version='1.0.0',
author='Brady Garvin',
author_email='bgarvin@cse.unl.edu',
description='A command-line tic-tac-toe game used to introduce SE practices and terminology in SOFT 162',
url='https://git.unl.edu/soft-core/soft-161-and-162/software-maintenance-example',
packages=setuptools.find_packages(exclude=(
'test',
)),
python_requires='>=3',
test_suite='test',
entry_points={
'console_scripts': [
'tic_tac_toe = tic_tac_toe.tic_tac_toe:main',
],
},
)
from unittest import TestCase, main
from tic_tac_toe.board import Mark, BOARD_SIZE, MARK_COUNT, Board
from tic_tac_toe.ai import ai
def create_board(*lines):
marks = [None] * MARK_COUNT
for y, line in enumerate(lines):
for x, character in enumerate(line):
try:
marks[y * BOARD_SIZE + x] = Mark(character)
except ValueError:
pass
return Board(marks)
class TestAI(TestCase):
def test_first_move(self):
board = create_board(
'012',
'345',
'678',
)
self.assertEqual(
ai.choose_move_and_description(board),
(4, 'The computer claims the center.'),
)
def test_second_move_versus_corner(self):
board = create_board(
'012',
'3O5',
'X78',
)
self.assertEqual(
ai.choose_move_and_description(board),
(2, 'The computer plays in the corner.'),
)
def test_third_move_versus_corner_and_adjacent_side(self):
board = create_board(
'01O',
'3O5',
'XX8',
)
self.assertEqual(
ai.choose_move_and_description(board),
(8, 'The computer blocks a win.'),
)
def test_fourth_move_versus_corner_and_two_sides(self):
board = create_board(
'01O',
'3OX',
'XXO',
)
self.assertEqual(
ai.choose_move_and_description(board),
(0, 'The computer wins.'),
)
def test_fourth_move_versus_two_corners_and_adjacent_side(self):
board = create_board(
'X1O',
'3O5',
'XXO',
)
self.assertEqual(
ai.choose_move_and_description(board),
(5, 'The computer wins.'),
)
def test_third_move_versus_two_corners(self):
board = create_board(
'01O',
'3O5',
'X7X',
)
self.assertEqual(
ai.choose_move_and_description(board),
(7, 'The computer blocks a win.'),
)
def test_fourth_move_versus_two_corners(self):
board = create_board(
'0XO',
'3O5',
'XOX',
)
self.assertEqual(
ai.choose_move_and_description(board),
(3, 'The computer threatens a win.'),
)
def test_fifth_move_versus_two_corners(self):
board = create_board(
'0XO',
'OOX',
'XOX',
)
self.assertEqual(
ai.choose_move_and_description(board),
(0, 'The computer gives up.'),
)
def test_third_move_versus_corner_and_far_side(self):
board = create_board(
'01O',
'3OX',
'X78',
)
self.assertEqual(
ai.choose_move_and_description(board),
(0, 'The computer threatens a win.'),
)
def test_fourth_move_versus_corner_and_two_far_sides(self):
board = create_board(
'OXO',
'3OX',
'X78',
)
self.assertEqual(
ai.choose_move_and_description(board),
(8, 'The computer wins.'),
)
def test_second_move_versus_side(self):
board = create_board(
'012',
'3O5',
'6X8',
)
self.assertEqual(
ai.choose_move_and_description(board),
(0, 'The computer plays in the corner.'),
)
def test_third_move_versus_side(self):
board = create_board(
'O12',
'3O5',
'6XX',
)
self.assertEqual(
ai.choose_move_and_description(board),
(6, 'The computer blocks a win.'),
)
if __name__ == '__main__':
main()
from unittest import TestCase, main
from tic_tac_toe.board import Mark, MARK_COUNT, Board
class TestBoard(TestCase):
def test_new_board_emptiness(self):
board = Board()
actual = tuple(board[index] for index in range(MARK_COUNT))
expected = tuple(None for _ in range(MARK_COUNT))
self.assertEqual(actual, expected)
def test_duplicate_mark_prevention(self):
board = Board()
board = board.play(0)
with self.assertRaises(ValueError) as context:
board.play(0)
self.assertEqual(str(context.exception), 'Cannot play at index 0, which is already occupied by an O')
def test_turn_alternation(self):
board = Board()
self.assertEqual(board.next_turn, Mark.O)
board = board.play(0)
self.assertEqual(board.next_turn, Mark.X)
board = board.play(1)
self.assertEqual(board.next_turn, Mark.O)
board = board.play(2)
self.assertEqual(board.next_turn, Mark.X)
def test_mark_alternation(self):
board = Board()
board = board.play(0)
board = board.play(1)
board = board.play(2)
self.assertEqual(board[0], Mark.O)
self.assertEqual(board[1], Mark.X)
self.assertEqual(board[2], Mark.O)
def test_blank_counting(self):
board = Board()
self.assertEqual(board.count_marks({0, 1, 2}), (0, 0))
def test_mark_counting(self):
board = Board()
board = board.play(0)
board = board.play(1)
board = board.play(2)
self.assertEqual(board.count_marks({0, 1, 2}), (2, 1))
def test_blank_and_mark_counting(self):
board = Board()
board = board.play(0)
board = board.play(1)
board = board.play(2)
self.assertEqual(board.count_marks({1, 2, 3}), (1, 1))
if __name__ == '__main__':
main()
Metadata-Version: 2.1
Name: tic-tac-toe
Version: 1.0.0
Summary: A command-line tic-tac-toe game used to introduce SE practices and terminology in SOFT 162
Home-page: https://git.unl.edu/soft-core/soft-161-and-162/software-maintenance-example
Author: Brady Garvin
Author-email: bgarvin@cse.unl.edu
License: UNKNOWN
Platform: UNKNOWN
Requires-Python: >=3
UNKNOWN
README.md
setup.py
test/test_ai.py
test/test_board.py
tic_tac_toe/__init__.py
tic_tac_toe/ai.py
tic_tac_toe/board.py
tic_tac_toe/tic_tac_toe.py
tic_tac_toe.egg-info/PKG-INFO
tic_tac_toe.egg-info/SOURCES.txt
tic_tac_toe.egg-info/dependency_links.txt
tic_tac_toe.egg-info/entry_points.txt
tic_tac_toe.egg-info/top_level.txt
\ No newline at end of file
[console_scripts]
tic_tac_toe = tic_tac_toe.tic_tac_toe:main
tic_tac_toe
File added
File added
File added
File added
from .board import MARK_COUNT, ROWS_AS_INDICES
class AIRule:
def __init__(self, pattern, minimum_occurrences, description):
self.pattern = pattern
self.minimum_occurrences = minimum_occurrences
self.description = description
def suggest_move(self, board):
occurrences_by_index = [0] * MARK_COUNT
for indices in ROWS_AS_INDICES:
if board.count_marks(indices) == self.pattern:
for index in indices:
occurrences_by_index[index] += 1
for index, occurrences in enumerate(occurrences_by_index):
if board[index] is None and occurrences >= self.minimum_occurrences:
return index
return None
class AI:
def __init__(self, rules):
self.rules = rules
def choose_move_and_description(self, board):
for rule in self.rules:
move = rule.suggest_move(board)
if move is not None:
return move, rule.description
assert False
ai = AI((
AIRule((2, 0), 1, 'The computer wins.'),
AIRule((0, 2), 1, 'The computer blocks a win.'),
AIRule((0, 0), 4, 'The computer claims the center.'),
AIRule((0, 0), 2, 'The computer plays in the corner.'),
AIRule((1, 0), 1, 'The computer threatens a win.'),
AIRule(None, 0, 'The computer gives up.'),
))
from enum import Enum
class Mark(Enum):
O = 'O'
X = 'X'
BOARD_SIZE = 3
MARK_COUNT = BOARD_SIZE * BOARD_SIZE
ROWS_AS_INDICES = tuple(tuple(y * BOARD_SIZE + x for x in range(BOARD_SIZE)) for y in range(BOARD_SIZE)) + \
tuple(tuple(y * BOARD_SIZE + x for y in range(BOARD_SIZE)) for x in range(BOARD_SIZE)) + \
(tuple(x * BOARD_SIZE + x for x in range(BOARD_SIZE)),) + \
(tuple((BOARD_SIZE - 1 - x) * BOARD_SIZE + x for x in range(BOARD_SIZE)),)
class Board:
def __init__(self, marks=None):
self._marks = marks if marks is not None else (None,) * MARK_COUNT
def __getitem__(self, index):
return self._marks[index]
def count_marks(self, indices):
marks_at_indices = tuple(self._marks[index] for index in indices)
return tuple(marks_at_indices.count(mark) for mark in Mark)
@property
def next_turn(self):
if None not in self._marks or any(BOARD_SIZE in self.count_marks(indices) for indices in ROWS_AS_INDICES):
return None
return Mark.O if self._marks.count(None) % 2 == 1 else Mark.X
def play(self, index):
if self._marks[index] is not None:
raise ValueError(
f'Cannot play at index {index}, which is already occupied by an {self._marks[index].value}'
)
new_marks = list(self._marks)
new_marks[index] = self.next_turn
return Board(tuple(new_marks))
def __str__(self):
result = ''
for y in range(BOARD_SIZE):
for x in range(BOARD_SIZE):
index = y * BOARD_SIZE + x
mark = self._marks[index]
result += mark.value if mark is not None else f'{index}'
result += '\n'
return result
#! /usr/bin/env python3
from .board import Mark, Board
from .ai import ai
def main():
print('New game of tic-tac-toe:')
print()
board = Board()
while board.next_turn is not None:
print(board)
if board.next_turn == Mark.O:
move, description = ai.choose_move_and_description(board)
board = board.play(move)
print(description)
print(f'Computer\'s move: {move}')
else:
while True:
try:
move = int(input('Your move: '))
board = board.play(move)
break
except (ValueError, IndexError):
print('Please enter a valid move.')
print()
print()
print(board)
print('Game over!')
if __name__ == '__main__':
main()
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment