Add the random-walk and spirograph challenges to the command line
turtle-challenges random-walk and turtle-challenges spirograph, the latter with --gap for the degrees between two circles. Task: MIL-004#4
This commit is contained in:
@@ -10,12 +10,17 @@ from functools import partial
|
|||||||
from turtle_challenges.constants import (
|
from turtle_challenges.constants import (
|
||||||
CHALLENGE_DASHED_LINE,
|
CHALLENGE_DASHED_LINE,
|
||||||
CHALLENGE_NAMES,
|
CHALLENGE_NAMES,
|
||||||
|
CHALLENGE_RANDOM_WALK,
|
||||||
CHALLENGE_SHAPES,
|
CHALLENGE_SHAPES,
|
||||||
|
CHALLENGE_SPIROGRAPH,
|
||||||
CHALLENGE_SQUARE,
|
CHALLENGE_SQUARE,
|
||||||
|
SPIROGRAPH_GAP_DEGREES,
|
||||||
)
|
)
|
||||||
from turtle_challenges.dashed_line import draw_dashed_line
|
from turtle_challenges.dashed_line import draw_dashed_line
|
||||||
from turtle_challenges.pen import Pen
|
from turtle_challenges.pen import Pen
|
||||||
|
from turtle_challenges.random_walk import random_walk
|
||||||
from turtle_challenges.shapes import draw_shapes
|
from turtle_challenges.shapes import draw_shapes
|
||||||
|
from turtle_challenges.spirograph import draw_spirograph
|
||||||
from turtle_challenges.square import draw_square
|
from turtle_challenges.square import draw_square
|
||||||
from turtle_challenges.window import (
|
from turtle_challenges.window import (
|
||||||
TurtleUnavailableError,
|
TurtleUnavailableError,
|
||||||
@@ -28,7 +33,7 @@ def build_parser() -> argparse.ArgumentParser:
|
|||||||
"""!
|
"""!
|
||||||
@brief Build the argument parser of the command line.
|
@brief Build the argument parser of the command line.
|
||||||
|
|
||||||
@return A parser with the challenge name.
|
@return A parser with the challenge name and the spirograph gap option.
|
||||||
"""
|
"""
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
prog="turtle-challenges",
|
prog="turtle-challenges",
|
||||||
@@ -40,14 +45,22 @@ def build_parser() -> argparse.ArgumentParser:
|
|||||||
choices=CHALLENGE_NAMES,
|
choices=CHALLENGE_NAMES,
|
||||||
help="the challenge to run",
|
help="the challenge to run",
|
||||||
)
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--gap",
|
||||||
|
type=float,
|
||||||
|
default=SPIROGRAPH_GAP_DEGREES,
|
||||||
|
help="spirograph only: degrees between two circles "
|
||||||
|
f"(default: {SPIROGRAPH_GAP_DEGREES})",
|
||||||
|
)
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
|
|
||||||
def build_challenges(pen: Pen) -> dict[str, Callable[[], None]]:
|
def build_challenges(pen: Pen, gap: float) -> dict[str, Callable[[], None]]:
|
||||||
"""!
|
"""!
|
||||||
@brief Bind every challenge to a turtle.
|
@brief Bind every challenge to a turtle.
|
||||||
|
|
||||||
@param pen The turtle that draws.
|
@param pen The turtle that draws.
|
||||||
|
@param gap Degrees between two circles of the spirograph.
|
||||||
@return A mapping from the command line name to a function without
|
@return A mapping from the command line name to a function without
|
||||||
arguments that draws the challenge.
|
arguments that draws the challenge.
|
||||||
"""
|
"""
|
||||||
@@ -55,6 +68,8 @@ def build_challenges(pen: Pen) -> dict[str, Callable[[], None]]:
|
|||||||
CHALLENGE_SQUARE: partial(draw_square, pen),
|
CHALLENGE_SQUARE: partial(draw_square, pen),
|
||||||
CHALLENGE_DASHED_LINE: partial(draw_dashed_line, pen),
|
CHALLENGE_DASHED_LINE: partial(draw_dashed_line, pen),
|
||||||
CHALLENGE_SHAPES: partial(draw_shapes, pen),
|
CHALLENGE_SHAPES: partial(draw_shapes, pen),
|
||||||
|
CHALLENGE_RANDOM_WALK: partial(random_walk, pen),
|
||||||
|
CHALLENGE_SPIROGRAPH: partial(draw_spirograph, pen, gap),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -72,6 +87,6 @@ def main(argv: Sequence[str] | None = None) -> int:
|
|||||||
tim = create_pen()
|
tim = create_pen()
|
||||||
except TurtleUnavailableError as error:
|
except TurtleUnavailableError as error:
|
||||||
parser.exit(1, f"{parser.prog}: error: {error}\n")
|
parser.exit(1, f"{parser.prog}: error: {error}\n")
|
||||||
build_challenges(tim)[args.challenge]()
|
build_challenges(tim, args.gap)[args.challenge]()
|
||||||
window.exitonclick()
|
window.exitonclick()
|
||||||
return 0
|
return 0
|
||||||
|
|||||||
@@ -92,9 +92,17 @@ CHALLENGE_DASHED_LINE = "dashed-line"
|
|||||||
## Command line name of challenge 3.
|
## Command line name of challenge 3.
|
||||||
CHALLENGE_SHAPES = "shapes"
|
CHALLENGE_SHAPES = "shapes"
|
||||||
|
|
||||||
|
## Command line name of challenge 4.
|
||||||
|
CHALLENGE_RANDOM_WALK = "random-walk"
|
||||||
|
|
||||||
|
## Command line name of challenge 5.
|
||||||
|
CHALLENGE_SPIROGRAPH = "spirograph"
|
||||||
|
|
||||||
## Names of the challenges on the command line, in the order of the course.
|
## Names of the challenges on the command line, in the order of the course.
|
||||||
CHALLENGE_NAMES: tuple[str, ...] = (
|
CHALLENGE_NAMES: tuple[str, ...] = (
|
||||||
CHALLENGE_SQUARE,
|
CHALLENGE_SQUARE,
|
||||||
CHALLENGE_DASHED_LINE,
|
CHALLENGE_DASHED_LINE,
|
||||||
CHALLENGE_SHAPES,
|
CHALLENGE_SHAPES,
|
||||||
|
CHALLENGE_RANDOM_WALK,
|
||||||
|
CHALLENGE_SPIROGRAPH,
|
||||||
)
|
)
|
||||||
|
|||||||
+19
-1
@@ -5,6 +5,7 @@ from turtle_challenges import cli
|
|||||||
from turtle_challenges.constants import (
|
from turtle_challenges.constants import (
|
||||||
CHALLENGE_NAMES,
|
CHALLENGE_NAMES,
|
||||||
DASH_COUNT,
|
DASH_COUNT,
|
||||||
|
SPIROGRAPH_GAP_DEGREES,
|
||||||
SQUARE_SIDES,
|
SQUARE_SIDES,
|
||||||
)
|
)
|
||||||
from turtle_challenges.pen import Pen, Window
|
from turtle_challenges.pen import Pen, Window
|
||||||
@@ -29,7 +30,7 @@ def fake_pen_and_window(monkeypatch: pytest.MonkeyPatch) -> tuple[FakePen, FakeW
|
|||||||
|
|
||||||
|
|
||||||
def test_every_challenge_name_has_a_challenge() -> None:
|
def test_every_challenge_name_has_a_challenge() -> None:
|
||||||
challenges = cli.build_challenges(FakePen())
|
challenges = cli.build_challenges(FakePen(), SPIROGRAPH_GAP_DEGREES)
|
||||||
|
|
||||||
assert set(challenges) == set(CHALLENGE_NAMES)
|
assert set(challenges) == set(CHALLENGE_NAMES)
|
||||||
|
|
||||||
@@ -39,6 +40,13 @@ def test_parser_accepts_every_challenge_name(name: str) -> None:
|
|||||||
args = cli.build_parser().parse_args([name])
|
args = cli.build_parser().parse_args([name])
|
||||||
|
|
||||||
assert args.challenge == name
|
assert args.challenge == name
|
||||||
|
assert args.gap == SPIROGRAPH_GAP_DEGREES
|
||||||
|
|
||||||
|
|
||||||
|
def test_parser_reads_the_spirograph_gap() -> None:
|
||||||
|
args = cli.build_parser().parse_args(["spirograph", "--gap", "7.5"])
|
||||||
|
|
||||||
|
assert args.gap == 7.5
|
||||||
|
|
||||||
|
|
||||||
def test_parser_rejects_an_unknown_challenge(
|
def test_parser_rejects_an_unknown_challenge(
|
||||||
@@ -82,6 +90,16 @@ def test_main_runs_the_dashed_line(
|
|||||||
assert len(pen.args_of("forward")) == 2 * DASH_COUNT
|
assert len(pen.args_of("forward")) == 2 * DASH_COUNT
|
||||||
|
|
||||||
|
|
||||||
|
def test_main_passes_the_gap_to_the_spirograph(
|
||||||
|
fake_pen_and_window: tuple[FakePen, FakeWindow],
|
||||||
|
) -> None:
|
||||||
|
pen, _ = fake_pen_and_window
|
||||||
|
|
||||||
|
cli.main(["spirograph", "--gap", "90"])
|
||||||
|
|
||||||
|
assert len(pen.args_of("circle")) == 4
|
||||||
|
|
||||||
|
|
||||||
def test_main_reports_a_missing_turtle_and_exits_with_status_1(
|
def test_main_reports_a_missing_turtle_and_exits_with_status_1(
|
||||||
monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str]
|
monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str]
|
||||||
) -> None:
|
) -> None:
|
||||||
|
|||||||
Reference in New Issue
Block a user