diff --git a/typescript/loopover/package.json b/typescript/loopover/package.json
index 4db8a6b..77d7287 100644
--- a/typescript/loopover/package.json
+++ b/typescript/loopover/package.json
@@ -22,7 +22,8 @@
     "author": "Matei Adriel",
     "license": "GPL-3.0",
     "dependencies": {
-        "@thi.ng/api": "^6.5.0"
+        "@thi.ng/api": "^6.5.0",
+        "immutable": "^4.0.0-rc.12"
     },
     "sideEffects": false,
     "devDependencies": {
diff --git a/typescript/loopover/pnpm-lock.yaml b/typescript/loopover/pnpm-lock.yaml
index 643c1f2..061f817 100644
--- a/typescript/loopover/pnpm-lock.yaml
+++ b/typescript/loopover/pnpm-lock.yaml
@@ -1,5 +1,6 @@
 dependencies:
   '@thi.ng/api': 6.5.0
+  immutable: 4.0.0-rc.12
 devDependencies:
   '@types/chai': 4.2.5
   '@types/mocha': 5.2.7
@@ -232,6 +233,10 @@ packages:
       node: '>=4'
     resolution:
       integrity: sha1-tdRU3CGZriJWmfNGfloH87lVuv0=
+  /immutable/4.0.0-rc.12:
+    dev: false
+    resolution:
+      integrity: sha512-0M2XxkZLx/mi3t8NVwIm1g8nHoEmM9p9UBl/G9k4+hm0kBgOVdMV/B3CY5dQ8qG8qc80NN4gDV4HQv6FTJ5q7A==
   /inflight/1.0.6:
     dependencies:
       once: 1.4.0
@@ -552,6 +557,7 @@ specifiers:
   '@types/mocha': ^5.2.7
   '@types/node': ^12.12.12
   chai: ^4.2.0
+  immutable: ^4.0.0-rc.12
   rimraf: ^3.0.0
   rollup: ^1.27.5
   rollup-plugin-commonjs: ^10.1.0
diff --git a/typescript/loopover/src/classes/Game.ts b/typescript/loopover/src/classes/Game.ts
new file mode 100644
index 0000000..b3bb66b
--- /dev/null
+++ b/typescript/loopover/src/classes/Game.ts
@@ -0,0 +1,45 @@
+import { assert } from '@thi.ng/api'
+import { List } from 'immutable'
+import { Direction } from '../types/direction'
+import { rangeArray } from '../helpers/rangeArray'
+
+export class GameState {
+    public height: number
+
+    public constructor(public cells: List<number>, public width: number) {
+        const height = cells.count() / width
+
+        assert(
+            height === Math.floor(height),
+            `Recived non-integer height: ${height}`
+        )
+
+        this.height = height
+    }
+
+    public moveX(direction: Direction, layers: Iterable<number>) {
+        const newState = this.cells.withMutations(list => {
+            for (const layer of layers) {
+                const slice = list.slice(
+                    layer * this.width,
+                    (layer + 1) * this.width
+                )
+
+                if (direction === -1) {
+                    const first = slice.first<number>()
+                    slice.shift()
+                    slice.push(first)
+                } else if (direction === 1) {
+                    const last = slice.last<number>()
+                    slice.unshift(last)
+                }
+
+                for (let i = 0; i < slice.count(); i++) {
+                    list[i + layer * this.width] = slice[i]
+                }
+            }
+        })
+
+        return new GameState(newState, this.width)
+    }
+}
diff --git a/typescript/loopover/src/helpers/chunkX.ts b/typescript/loopover/src/helpers/chunkX.ts
new file mode 100644
index 0000000..d9e9bea
--- /dev/null
+++ b/typescript/loopover/src/helpers/chunkX.ts
@@ -0,0 +1,6 @@
+import { rangeArray } from './rangeArray'
+
+export const chunkX = <T>(arr: T[], width: number, height: number): T[][] =>
+    rangeArray(0, this.height).map(index =>
+        arr.slice(index * height, (index + 1) * width)
+    )
diff --git a/typescript/loopover/src/helpers/rangeArray.ts b/typescript/loopover/src/helpers/rangeArray.ts
new file mode 100644
index 0000000..e137737
--- /dev/null
+++ b/typescript/loopover/src/helpers/rangeArray.ts
@@ -0,0 +1,4 @@
+export const rangeArray = (start: number, end: number) =>
+    Array(end - start)
+        .fill(true)
+        .map((_, i) => i + start)
diff --git a/typescript/loopover/src/types/direction.ts b/typescript/loopover/src/types/direction.ts
new file mode 100644
index 0000000..b696ffb
--- /dev/null
+++ b/typescript/loopover/src/types/direction.ts
@@ -0,0 +1 @@
+export type Direction = -1 | 1