Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Top Level Walruses #10

Open
wants to merge 101 commits into
base: main
Choose a base branch
from
Open

Top Level Walruses #10

wants to merge 101 commits into from

Conversation

janine9vn
Copy link
Contributor

No description provided.

Copy link
Contributor Author

@janine9vn janine9vn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Quality

The code itself is very well done. In particular some of the extra work gone into the files in common.py is appreciated, to clearly define non-standard behavior and avoid messy hacks that would otherwise be necessary. The typehints are well done and consistent as well. The code is logical and (mostly) easy to follow and reason about.

Documentation

One aspect that was lacking in this project was documentation. Many functions did not have docstrings, and when they did, they often felt lacking. Some of the more dense code as well could have substantially benefitted from comments explaining what the intent is and some of the more technical details. I did not comment on every instance, but it was pretty much project-wide and did make this project a bit harder to review.

The README/presentation was well done. I appreciate the demo examples included and how everything was explained. The one aspect I think could be improved in general for the README, or in the code itself, is a more robust explanation of the different components of the Piet interpreter, generator, reader, runtime, and the common files. There's a lot going on there and could be benefitted from more words to help explain what's happening.

Commit Messages

The commit messages overall were good. Some could have been a bit more descriptive about what the commit itself accomplished. For instance "piet interpreter almost finished", could instead describe what was added to get the piet interpreter to an almost finished state. In general, for more substantial commits, I'm a fan of being more specific about what was added in the commit body.

Summary

This is a very impressive project, with a fully working interpreter and generator for Piet. The lack of documentation is a sore spot, but otherwise I don't have major comments. The organization and structure of the project as well is nicely done and logical. It made it easier to review. Additionally, I do appreciate the inclusion of tests. This code jam is usually a time crunch, so including tests is going a bit above and beyond.

A quick note...

Thank you to dawn and aboo for reviewing parts of this project and providing some of the comments I left.

def generate(
input_path: Path,
output_path: Path,
cols: Optional[int] = None,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is minor, but the slightly recommended way to do this typehint would be: int | None

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIRC Optional is here because Typer does not support the new-style annotation type | None.

print("\n")
print("Successfully generated a Piet program!")
print(f"Image saved to: {output_path}")
print(f"You can run it with: {command}")
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a nice QoL feature for the user, that you show the command they can run.

Comment on lines +26 to +41
if input_path.exists():
data = input_path.read_bytes()
else:
data = str(input_path).encode()
command = f'python -m {__package__} run "{output_path}"'
first_input = next(iter(input), "")
generator = ImageGenerator(input=first_input, step_limit=step_limit, debug=debug)
if recurse:
image = generator.generate_recursive(data, (v.encode() for v in input), recurse)
command += " --execute"
else:
key = first_input.encode()
if key:
key *= len(data) // len(key) + 1
command += f' --input "{first_input}"'
image = generator.generate_image(data, key, cols)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this function could use a docstring or some comments in this area about what's happening. It's doing a fair bit and not all of it necessarily straightforward.

T = TypeVar("T")


class SelfExpandingList(list[T]):
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This class is doing quite a bit. It is in desperate need of some explainer comments about what its purpose is and what it is trying to do.

You can piece together what it is doing and its purpose after a bit, but comments would go a long way.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be honest, this was more of a last-ditch attempt to facilitate a dynamically sized 2-dimentional array without lengthy blocks of logic and append calls.

Comment on lines +114 to +116
# lightness hue
# v v
PIET_COLORS: tuple[tuple[Color, ...], ...] = (
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cute comment and very helpful

height, width = len(colors), len(colors[0])
smallest = height

rsf = 0
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is rsf? It is unclear in this section what it is intended to represent.

Comment on lines +132 to +134
for y in range(height):
for x in range(width):
color_ints.append(int(colors[y][x]))
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be replaced with some functions from itertools:

import itertools

...

for color in itertools.chain.from_iterable(colors):
  color_ints.append(int(color))

or, depending on what width is, just itertools.product

Comment on lines +78 to +82
else:
if rsf < smallest:
smallest = rsf
pixel = colors[y][x]
rsf = 1
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be simplified into an elif

Comment on lines +34 to +42
def __repr__(self) -> str:
return (
f"StepTrace({self.iteration:0>4},"
f" instruction={self.instruction.__name__},"
f" position={self.position},"
f" did_flip={self.did_flip},"
f" last_codel={self.last_codel},"
f" current_codel={self.current_codel})"
)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very nice usage of a repr here!

Comment on lines +12 to +16
class PointerDirection(Enum):
RIGHT = OrderedPair(0, 1)
DOWN = OrderedPair(1, 0)
LEFT = OrderedPair(0, -1)
UP = OrderedPair(-1, 0)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a nice way of handling directions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants