diff --git a/robotgame/logic/rollingstone.py b/robotgame/logic/rollingstone.py
new file mode 100644
index 0000000..487a048
--- /dev/null
+++ b/robotgame/logic/rollingstone.py
@@ -0,0 +1,89 @@
+#!/usr/bin/env python
+
+# This file is part of ROBOTGAME
+#
+# ROBOTGAME is free software: you can redistribute it and/or modify it under the
+# terms of the GNU General Public License as published by the Free Software
+# Foundation, either version 3 of the License, or (at your option) any later
+# version.
+#
+# ROBOTGAME is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along with
+# ROBOTGAME. If not, see .
+#
+# ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' '
+#
+# rollingstone.py
+# --------------------
+# date created : Tue Aug 7 2012
+# copyright : (C) 2012 Niels G. W. Serup
+# maintained by : Niels G. W. Serup
+
+"""Logic for rolling."""
+
+from __future__ import print_function
+
+class RollingStoneError(Exception):
+ pass
+
+class Field(object):
+ def next_posdir(self):
+ raise NotImplementedError
+
+class Start(Field):
+ def __init__(self, direction):
+ self.direction = direction
+
+ def next_posdir(self, pos, direc):
+ return self.direction.next_pos(pos), self.direction
+
+class Turn(Field):
+ def __init__(self, direction):
+ self.direction = direction
+
+ def next_posdir(self, pos, direc):
+ return self.direction.next_pos(pos), self.direction
+
+class Goal(Field):
+ def next_posdir(self, pos, direc):
+ return pos, direc
+
+class Stone(Field):
+ def next_posdir(self, pos, direc):
+ return pos, direc
+
+
+def step(playfield, pos, direc):
+ field = _at(playfield, pos)
+ if field is not None:
+ return field.next_posdir(pos, direc)
+ return direc.next_pos(pos), direc
+
+def reaches_goal(playfield, max_steps):
+ pos = _find_start(playfield)
+ direc = None
+ for i in range(max_steps):
+ pos, direc = step(playfield, pos, direc)
+ if isGoal(playfield, pos):
+ return True
+ if isStone(playfield, pos):
+ return False
+ return False
+
+def _find_start(playfield):
+ for y in range(len(playfield)):
+ for x in range(len(playfield[y])):
+ if isStart(playfield, (x, y)):
+ return (x, y)
+ raise RollingStoneError("Missing Start field")
+
+def _at(playfield, pos):
+ x, y = pos
+ return playfield[y][x]
+
+_is = lambda t: lambda playfield, pos: isinstance(_at(playfield, pos), t)
+
+(isGoal, isTurn, isStart, isStone) = (_is(Goal), _is(Turn), _is(Start), _is(Stone))
diff --git a/tests/rollingstone_tests.py b/tests/rollingstone_tests.py
new file mode 100644
index 0000000..e728447
--- /dev/null
+++ b/tests/rollingstone_tests.py
@@ -0,0 +1,27 @@
+
+import unittest
+from robotgame.logic.rollingstone import *
+from robotgame.logic.direction import *
+
+
+playfield_example_succeed = [
+ [Start(Down), None, None, None ],
+ [None, None, Stone(), None ],
+ [Turn(Right), None, Turn(Down), None ],
+ [None, Stone(), Turn(Right), Goal()],
+ ]
+
+playfield_example_fail = [
+ [Start(Down), None, None, None ],
+ [None, None, Stone(), None ],
+ [Turn(Right), Stone(), Turn(Down), None ],
+ [None, None, Turn(Right), Goal()],
+ ]
+
+class RollingStoneTest(unittest.TestCase):
+ def test_playfield(self):
+ self.assertTrue(reaches_goal(playfield_example_succeed, 100))
+ self.assertFalse(reaches_goal(playfield_example_fail, 100))
+
+if __name__ == '__main__':
+ unittest.main()