data Instruction = Forward Distance
| Clockwise
| Anticlockwise
- | Comment String
deriving (Show, Eq)
part1 = length
part2 :: [Instruction] -> Int
-part2 instructions = finalDistance $ executeAll instructions
+part2 = finalDistance . executeAll
where executeAll = foldl' execute initialMowmaster
-initialMowmaster = Mowmaster North (0, 0)
+initialMowmaster = Mowmaster East (0, 0)
-- Calculate manhattan distance from start to this state
execute m (Forward s) = m {position = forward s (direction m) (position m)}
execute m Clockwise = m {direction = turnCW (direction m)}
execute m Anticlockwise = m {direction = turnACW (direction m)}
-execute m _ = m
-- Move in the current direction
-forward :: Int -> Direction -> Position -> Position
+forward :: Distance -> Direction -> Position -> Position
forward s North (e, n) = (e, n+s)
forward s South (e, n) = (e, n-s)
forward s West (e, n) = (e-s, n)
"cells": [
{
"cell_type": "code",
- "execution_count": 1,
+ "execution_count": 20,
"metadata": {},
"outputs": [],
"source": [
"cell_type": "markdown",
"metadata": {},
"source": [
- "# Part 1"
+ "# Part 1\n",
+ "`startswith()` isn't useful that often, but it makes for easily readable code when it is."
]
},
{
"cell_type": "code",
- "execution_count": 3,
+ "execution_count": 31,
"metadata": {},
"outputs": [
{
"1136"
]
},
- "execution_count": 3,
+ "execution_count": 31,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "non_comments = 0\n",
+ "for i in instructions:\n",
+ " if not i.startswith('#'):\n",
+ " non_comments += 1\n",
+ "non_comments"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "This sort if simple walking along a list is sometimes easier with a list comprehension. \n",
+ "\n",
+ "In this case, I transform each element into the number 1, but only if it's not a comment. Then I sum all the 1s, which is the number of non-comment lines."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 21,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "1136"
+ ]
+ },
+ "execution_count": 21,
"metadata": {},
"output_type": "execute_result"
}
},
{
"cell_type": "code",
- "execution_count": 4,
+ "execution_count": 22,
"metadata": {},
"outputs": [
{
"1136"
]
},
- "execution_count": 4,
+ "execution_count": 22,
"metadata": {},
"output_type": "execute_result"
}
"sum(1 for i in open('../../data/01-mowmaster.txt') if not i.startswith('#'))"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "How many comments and total lines?"
+ ]
+ },
{
"cell_type": "code",
- "execution_count": 5,
+ "execution_count": 23,
"metadata": {},
"outputs": [
{
"395"
]
},
- "execution_count": 5,
+ "execution_count": 23,
"metadata": {},
"output_type": "execute_result"
}
},
{
"cell_type": "code",
- "execution_count": 6,
+ "execution_count": 24,
"metadata": {},
"outputs": [
{
"1531"
]
},
- "execution_count": 6,
+ "execution_count": 24,
"metadata": {},
"output_type": "execute_result"
}
"cell_type": "markdown",
"metadata": {},
"source": [
- "An initial mower. The initial location and direction don't matter in this case, so I choose a location that's easy and an arbitrary location."
+ "An initial mower. I adopt the Scratch convention that a heading of 0 degrees is up, increasing clockwise; so right is 90 degrees."
]
},
{
"cell_type": "code",
- "execution_count": 12,
+ "execution_count": 25,
"metadata": {},
"outputs": [],
"source": [
"cell_type": "markdown",
"metadata": {},
"source": [
- "Execute the instructions. If it starts `C` or `A`, turn; if it starts `F`, move forward. Ignore all other instructions."
+ "Execute the instructions. Do them one at a time, updating the mowmaster for each one.\n",
+ "\n",
+ "(Can you see an accumulator pattern again?)"
]
},
{
"cell_type": "code",
- "execution_count": 13,
+ "execution_count": 26,
"metadata": {},
"outputs": [],
"source": [
"def execute(mowmaster, instructions, debug=False):\n",
" for instruction in instructions:\n",
- " if instruction == 'C':\n",
- " mowmaster['d'] = (mowmaster['d'] + 90) % 360 # Use the modul\n",
- " elif instruction == 'A':\n",
- " mowmaster['d'] = (mowmaster['d'] - 90) % 360\n",
- " elif instruction.startswith('F'):\n",
- " mowmaster = move(mowmaster, int(instruction[1:]))\n",
- " if debug: \n",
- " print(instruction, mowmaster)\n",
+ " mowmaster = execute_one(mowmaster, instruction, debug=debug)\n",
+ " return mowmaster"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Execute one instruction. If it starts `C` or `A`, turn; if it starts `F`, move forward. Ignore all other instructions."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 27,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def execute_one(mowmaster, instruction, debug=False):\n",
+ " if instruction == 'C':\n",
+ " mowmaster['d'] = (mowmaster['d'] + 90) % 360 # Use the modulus operator to keep the angle in the range 0--360\n",
+ " elif instruction == 'A':\n",
+ " mowmaster['d'] = (mowmaster['d'] - 90) % 360\n",
+ " elif instruction.startswith('F'):\n",
+ " mowmaster = move(mowmaster, int(instruction[1:]))\n",
+ " if debug: \n",
+ " print(instruction, mowmaster)\n",
" return mowmaster"
]
},
"cell_type": "markdown",
"metadata": {},
"source": [
- "The "
+ "The movement. Update `x` or `y` depending on the direction."
]
},
{
"cell_type": "code",
- "execution_count": 7,
+ "execution_count": 28,
"metadata": {},
"outputs": [],
"source": [
" return mower"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Finally, how far the Mowmaster has moved."
+ ]
+ },
{
"cell_type": "code",
- "execution_count": 16,
+ "execution_count": 29,
"metadata": {},
"outputs": [],
"source": [
" return abs(mw['x']) + abs(mw['y'])"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Now we have all the parts, put them together into the solution."
+ ]
+ },
{
"cell_type": "code",
- "execution_count": 18,
+ "execution_count": 30,
"metadata": {},
"outputs": [
{
"337"
]
},
- "execution_count": 18,
+ "execution_count": 30,
"metadata": {},
"output_type": "execute_result"
}