Skip to content

Commit 1e8258e

Browse files
authored
Merge pull request #71 from sjtrny/equality-checks
Improve eq checks and compare_output
2 parents 2ecd325 + 80053b0 commit 1e8258e

11 files changed

+108
-14
lines changed

stanfordkarel/karel_ascii.py

+22-2
Original file line numberDiff line numberDiff line change
@@ -221,14 +221,34 @@ def symmetric_difference(
221221
f"Student: {(first.avenue, first.street)}\n"
222222
f"Expected: {(second.avenue, second.street)}\n\n"
223223
)
224-
if first.world.beepers != second.world.beepers:
224+
225+
if first.direction != second.direction:
226+
result += (
227+
"Karel not facing the correct direction.\n"
228+
f"Student: {first.direction}\n"
229+
f"Expected: {second.direction}\n\n"
230+
)
231+
232+
if first.world.get_beepers() != second.world.get_beepers():
225233
extra_a, extra_b = symmetric_difference(
226-
first.world.beepers, second.world.beepers
234+
first.world.get_beepers(), second.world.get_beepers()
227235
)
228236
result += (
229237
"Beepers do not match: "
230238
"(Only beepers that appear in one world but not the other are listed)\n"
231239
f"Student: {extra_a}\n"
232240
f"Expected: {extra_b}\n\n"
233241
)
242+
243+
if first.world.get_colors() != second.world.get_colors():
244+
extra_a, extra_b = symmetric_difference(
245+
first.world.get_colors(), second.world.get_colors()
246+
)
247+
result += (
248+
"Colors do not match: "
249+
"(Only colors that appear in one world but not the other are listed)\n"
250+
f"Student: {extra_a}\n"
251+
f"Expected: {extra_b}\n\n"
252+
)
253+
234254
return result

stanfordkarel/karel_program.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,9 @@ def beepers_present(self) -> bool:
289289
beepers_on_corner (Bool) - True if there's at least one beeper
290290
on Karel's current corner, False otherwise
291291
"""
292-
return self.world.beepers[(self.avenue, self.street)] != 0
292+
if (self.avenue, self.street) in self.world.beepers:
293+
return self.world.beepers[(self.avenue, self.street)] != 0
294+
return False
293295

294296
def no_beepers_present(self) -> bool:
295297
return not self.beepers_present()

stanfordkarel/karel_world.py

+13-9
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@
4141

4242
from __future__ import annotations
4343

44-
import collections
4544
import copy
4645
import re
4746
import sys
@@ -90,10 +89,10 @@ def __init__(self, world_file: str) -> None:
9089
self.world_file = self.process_world(world_file)
9190

9291
# Map of beeper locations to the count of beepers at that location
93-
self.beepers: dict[tuple[int, int], int] = collections.defaultdict(int)
92+
self.beepers: dict[tuple[int, int], int] = {}
9493

9594
# Map of corner colors, defaults to ""
96-
self.corner_colors: dict[tuple[int, int], str] = collections.defaultdict(str)
95+
self.corner_colors: dict[tuple[int, int], str] = {}
9796

9897
# Set of Wall objects placed in the world
9998
self.walls: set[Wall] = set()
@@ -120,10 +119,10 @@ def __init__(self, world_file: str) -> None:
120119
def __eq__(self, other: object) -> bool:
121120
if isinstance(other, KarelWorld):
122121
return (
123-
self.beepers == other.beepers
124-
and self.walls == other.walls
125-
and self.num_streets == other.num_streets
122+
self.num_streets == other.num_streets
126123
and self.num_avenues == other.num_avenues
124+
and self.walls == other.walls
125+
and self.beepers == other.beepers
127126
and self.corner_colors == other.corner_colors
128127
)
129128
return NotImplemented
@@ -263,7 +262,10 @@ def load_from_file(self) -> None:
263262

264263
elif keyword == "beeper":
265264
# add the specified number of beepers to the world
266-
self.beepers[params["location"]] += params["val"]
265+
if params["location"] in self.beepers:
266+
self.beepers[params["location"]] += params["val"]
267+
else:
268+
self.beepers[params["location"]] = params["val"]
267269

268270
elif keyword == "karel":
269271
# Give Karel initial state values
@@ -315,7 +317,9 @@ def paint_corner(self, avenue: int, street: int, color: str) -> None:
315317
self.corner_colors[(avenue, street)] = color
316318

317319
def corner_color(self, avenue: int, street: int) -> str:
318-
return self.corner_colors[(avenue, street)]
320+
if (avenue, street) in self.corner_colors:
321+
return self.corner_colors[(avenue, street)]
322+
return ""
319323

320324
def reset_corner(self, avenue: int, street: int) -> None:
321325
self.beepers[(avenue, street)] = 0
@@ -331,7 +335,7 @@ def in_bounds(self, avenue: int, street: int) -> bool:
331335
def reset_world(self) -> None:
332336
"""Reset initial state of beepers in the world"""
333337
self.beepers = copy.deepcopy(self.init_beepers)
334-
self.corner_colors = collections.defaultdict(str)
338+
self.corner_colors = {}
335339

336340
def reload_world(self, filename: str | None = None) -> None:
337341
"""Reloads world using constructor."""
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

tests/exceptions_test.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@
2626

2727
@pytest.mark.parametrize(("code_file", "expected_error"), TEST_CASES)
2828
def test_exceptions(tmp_path: Path, code_file: str, expected_error: str) -> None:
29-
txt_file_contents = Path(f"tests/programs/{code_file}").read_text(encoding="utf-8")
29+
txt_file_contents = Path(f"tests/exception_programs/{code_file}").read_text(
30+
encoding="utf-8"
31+
)
3032
py_path = (tmp_path / code_file).with_suffix(".py")
3133
py_path.write_text(txt_file_contents)
3234
execute_karel_code(
@@ -39,7 +41,7 @@ def test_file_coverage() -> None:
3941
tested_files = {filepath for filepath, _ in TEST_CASES}
4042
test_programs = {
4143
filepath.name
42-
for filepath in Path("tests/programs").glob("*.txt")
44+
for filepath in Path("tests/exception_programs").glob("*.txt")
4345
if filepath.name not in IGNORED_FILES
4446
}
4547
untested_files = tested_files ^ test_programs

tests/karel_world_test.py

+46
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from pathlib import Path
22

3+
from stanfordkarel.karel_application import StudentCode
34
from stanfordkarel.karel_program import KarelProgram
45

56
STONE_MASON_ASCII_OUTPUT = (
@@ -87,3 +88,48 @@ def test_save_to_file(tmp_path: Path) -> None:
8788
)
8889

8990
assert output_file.read_text() == "\n".join(expected) + "\n"
91+
92+
@staticmethod
93+
def test_empty_beepers(tmp_path: Path) -> None:
94+
code_file = "empty_beeper.txt"
95+
txt_file_contents = Path(f"tests/programs/{code_file}").read_text(
96+
encoding="utf-8"
97+
)
98+
py_path = (tmp_path / code_file).with_suffix(".py")
99+
py_path.write_text(txt_file_contents)
100+
101+
test_program = KarelProgram("1x1")
102+
103+
test_code = StudentCode(py_path)
104+
test_code.inject_namespace(test_program)
105+
test_code.main()
106+
107+
ref_program = KarelProgram("1x1")
108+
109+
assert ref_program.world.beepers == test_program.world.beepers
110+
111+
@staticmethod
112+
def test_empty_colors(tmp_path: Path) -> None:
113+
code_file = "empty_color.txt"
114+
txt_file_contents = Path(f"tests/programs/{code_file}").read_text(
115+
encoding="utf-8"
116+
)
117+
py_path = (tmp_path / code_file).with_suffix(".py")
118+
py_path.write_text(txt_file_contents)
119+
120+
test_program = KarelProgram("1x1")
121+
122+
test_code = StudentCode(py_path)
123+
test_code.inject_namespace(test_program)
124+
test_code.main()
125+
126+
ref_program = KarelProgram("1x1")
127+
128+
assert ref_program.world.corner_colors == test_program.world.corner_colors
129+
130+
@staticmethod
131+
def test_equal_worlds() -> None:
132+
test_program = KarelProgram("1x1")
133+
ref_program = KarelProgram("1x1")
134+
135+
assert ref_program.world == test_program.world

tests/programs/empty_beeper.txt

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
from stanfordkarel import *
2+
3+
4+
def main():
5+
if corner_color_is(PINK):
6+
turn_left()
7+
8+
9+
if __name__ == "__main__":
10+
run_karel_program()

tests/programs/empty_color.txt

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
from stanfordkarel import *
2+
3+
4+
def main():
5+
if beepers_present():
6+
turn_left()
7+
8+
9+
if __name__ == "__main__":
10+
run_karel_program()

0 commit comments

Comments
 (0)