Skip to content

Commit c23d6c5

Browse files
committed
Merge branch 'feature/optimizations'
2 parents 46fe722 + 7d51193 commit c23d6c5

File tree

6 files changed

+159
-7
lines changed

6 files changed

+159
-7
lines changed

package.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ executables:
4747

4848
tests:
4949
icfpc2018-test:
50-
main: Spec.hs
50+
main: Test.hs
5151
source-dirs: test
5252
ghc-options:
5353
- -threaded

src/Optimizations.hs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
-- | Optimizations for sequences of commands.
2+
--
3+
-- Each optimizations replaces first occurrence of a pattern with an optimized
4+
-- version. It's up to you to figure out if the result is better than the
5+
-- input, and if you should re-run optimizations to find new occurrences of
6+
-- patterns.
7+
--
8+
-- WARNING: For now, these functions assume that there is only one nanobot!
9+
module Optimizations where
10+
11+
import Control.Monad (forM)
12+
13+
import Trace
14+
15+
import Debug.Trace
16+
17+
-- | Returns all possible optimizations of a given command sequence.
18+
optimize :: [Command] -> [[Command]]
19+
optimize [] = []
20+
optimize cmds = go [cmds] [cmds]
21+
where
22+
go :: [[Command]] -> [[Command]] -> [[Command]]
23+
go [] optimized = optimized
24+
go (t:stack) optimized =
25+
let
26+
new = map (\f -> f t) optimizers
27+
new' = filter (/= t) new
28+
in go (new' ++ stack) (new' ++ optimized)
29+
30+
optimizers :: [[Command] -> [Command]]
31+
optimizers = [splitLMove, mergeSMoves]
32+
33+
-- | Turns [LMove a b, Wait] into [SMove a, SMove b]
34+
--
35+
-- The latter consumes 4 less energy points than the former.
36+
--
37+
-- Note: it doesn't make sense to split [LMove a b] into two SMoves because the
38+
-- extra step will consume 3 R³ energy points, which is detrimental for any
39+
-- R greater than one.
40+
splitLMove :: [Command] -> [Command]
41+
splitLMove [] = []
42+
splitLMove ((LMove a b):Wait:cmds) =
43+
(SMove (fromShortLinDiff a)) : (SMove (fromShortLinDiff b)) : cmds
44+
splitLMove (cmd:cmds) = cmd : (splitLMove cmds)
45+
46+
-- | Turns [SMove a, SMove b] into [SMove x] if a and b are on the same axis
47+
-- and can be combined.
48+
mergeSMoves :: [Command] -> [Command]
49+
mergeSMoves [] = []
50+
mergeSMoves (m1@(SMove (LongLinDiff a1 d1)):m2@(SMove (LongLinDiff a2 d2)):cmds)
51+
| (a1 /= a2) || (d1 + d2 < -15) || (d1 + d2 > 15)
52+
= m1 : (mergeSMoves (m2:cmds))
53+
| otherwise =
54+
let d = d1 + d2
55+
in if d == 0
56+
then cmds
57+
else (SMove (LongLinDiff a1 d)) : cmds
58+
mergeSMoves (cmd:cmds) = cmd : (mergeSMoves cmds)

src/Trace.hs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,12 @@ instance Coded Command where
167167

168168
| otherwise = error "Command.decode: invalid input"
169169

170+
-- | Converts short linear difference into a long linear difference.
171+
--
172+
-- This conversion is valid since any short linear difference satisfies
173+
-- mlen(ld) ≤ 5, which also satisfies LongLinDiff's predicate of mlen(ld) ≤ 15.
174+
fromShortLinDiff :: ShortLinDiff -> LongLinDiff
175+
fromShortLinDiff (ShortLinDiff axis diff) = LongLinDiff axis diff
170176

171177
encodeL :: Coded a => a -> L.ByteString
172178
encodeL x = runPutL . runEncode $ encode x >> flush

test/Test.hs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import Test.Tasty
2+
3+
import TestSpec
4+
import TestOptimizations
5+
6+
main :: IO ()
7+
main = defaultMain tests
8+
9+
tests :: TestTree
10+
tests = testGroup "Tests" [specExamples, codec, optimizations]

test/TestOptimizations.hs

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
module TestOptimizations where
2+
3+
import Test.Tasty
4+
import Test.Tasty.HUnit
5+
6+
import Optimizations
7+
import Trace
8+
9+
optimizations :: TestTree
10+
optimizations = testGroup "Optimizations"
11+
[ testSplitLMove
12+
, testMergeSMoves
13+
]
14+
15+
testSplitLMove :: TestTree
16+
testSplitLMove = testGroup "splitLMove"
17+
[
18+
testCase "Empty input" $ splitLMove [] @?= []
19+
20+
, testCase "Unoptimal input" $
21+
let
22+
a = ShortLinDiff X 3
23+
a' = fromShortLinDiff a
24+
b = ShortLinDiff Z (-1)
25+
b' = fromShortLinDiff b
26+
in splitLMove [LMove a b, Wait] @?= [SMove a', SMove b']
27+
28+
, testCase "Only first occurrence is optimized" $
29+
let
30+
a = ShortLinDiff X 3
31+
a' = fromShortLinDiff a
32+
b = ShortLinDiff Z (-1)
33+
b' = fromShortLinDiff b
34+
in
35+
splitLMove [LMove a b, Wait, LMove a b, Wait]
36+
@?= [SMove a', SMove b', LMove a b, Wait]
37+
]
38+
39+
testMergeSMoves :: TestTree
40+
testMergeSMoves = testGroup "mergeSMoves"
41+
[ testCase "Empty input" $ mergeSMoves [] @?= []
42+
43+
, testCase "SMoves on different axis" $
44+
let
45+
m1 = SMove (LongLinDiff X 13)
46+
m2 = SMove (LongLinDiff Y (-11))
47+
in mergeSMoves [m1, m2] @?= [m1, m2]
48+
49+
, testCase "Long SMoves can't be combined" $
50+
let
51+
m1 = SMove (LongLinDiff X 13)
52+
m2 = SMove (LongLinDiff X 11)
53+
in mergeSMoves [m1, m2] @?= [m1, m2]
54+
55+
, testCase "Unoptimal input.1" $
56+
let
57+
m1 = SMove (LongLinDiff X 4)
58+
m2 = SMove (LongLinDiff X 3)
59+
result = SMove (LongLinDiff X 7)
60+
in mergeSMoves [m1, m2] @?= [result]
61+
, testCase "Unoptimal input.2" $
62+
let
63+
m1 = SMove (LongLinDiff Z (-11))
64+
m2 = SMove (LongLinDiff Z (-4))
65+
result = SMove (LongLinDiff Z (-15))
66+
in mergeSMoves [m1, m2] @?= [result]
67+
, testCase "Unoptimal input.3" $
68+
let
69+
m1 = SMove (LongLinDiff Y (-3))
70+
m2 = SMove (LongLinDiff Y 3)
71+
in mergeSMoves [m1, m2] @?= []
72+
73+
, testCase "Only first occurrence is optimized" $
74+
let
75+
m1 = SMove (LongLinDiff X 4)
76+
m2 = SMove (LongLinDiff X 3)
77+
combined = SMove (LongLinDiff X 7)
78+
in mergeSMoves [m1, m2, m1, m2] @?= [combined, m1, m2]
79+
]

test/Spec.hs renamed to test/TestSpec.hs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
{-# LANGUAGE BinaryLiterals #-}
22

3+
module TestSpec (
4+
specExamples,
5+
codec
6+
) where
7+
38
import Test.Tasty
49
import Test.Tasty.HUnit
510

@@ -15,12 +20,6 @@ assert :: String -> Bool -> IO ()
1520
assert _ True = return ()
1621
assert message False = fail message
1722

18-
main :: IO ()
19-
main = defaultMain tests
20-
21-
tests :: TestTree
22-
tests = testGroup "Tests" [specExamples, codec]
23-
2423
specExamples :: TestTree
2524
specExamples = testGroup "Specification examples"
2625
[

0 commit comments

Comments
 (0)