diff --git a/src/com/patryk/mathdoku/ArrayConversions.java b/src/com/patryk/mathdoku/ArrayConversions.java
new file mode 100644
index 0000000000000000000000000000000000000000..fb7c3c16cf3e2c4d272d018e191d0762aabb9ccb
--- /dev/null
+++ b/src/com/patryk/mathdoku/ArrayConversions.java
@@ -0,0 +1,42 @@
+package com.patryk.mathdoku;
+
+public class ArrayConversions {
+    public static float[] toNative(Float[] arr) {
+        float[] out = new float[arr.length];
+        for(int i = 0; i < arr.length; ++i) {
+            out[i] = arr[i];
+        }
+
+        return out;
+    }
+
+    public static int[] toNative(Object[] arr) {
+
+        int[] out = new int[arr.length];
+        for(int i = 0; i < arr.length; ++i) {
+            out[i] = (Integer)arr[i];
+        }
+
+        return out;
+    }
+
+    public static double[] toDouble(float[] arr) {
+        double[] out = new double[arr.length];
+        for(int i = 0; i < arr.length; ++i) {
+            out[i] = arr[i];
+        }
+
+        return out;
+    }
+
+    public static Float[] fromNative(float[] arr) {
+        Float[] out = new Float[arr.length];
+        for(int i = 0; i < arr.length; ++i) {
+            out[i] = arr[i];
+        }
+
+        return out;
+    }
+
+}
+
diff --git a/src/com/patryk/mathdoku/MathDoku.java b/src/com/patryk/mathdoku/MathDoku.java
index d6c0d7a8c49d497c259ccf2aa86f132c9fc9aa92..3910f26faf00ac9f56f4b3d7890cbe57c5fd9347 100755
--- a/src/com/patryk/mathdoku/MathDoku.java
+++ b/src/com/patryk/mathdoku/MathDoku.java
@@ -1,8 +1,14 @@
 package com.patryk.mathdoku;
 
+import com.patryk.mathdoku.cageData.Cage;
+import com.patryk.mathdoku.cageData.CageData;
 import com.patryk.mathdoku.cageData.DataFormatException;
+import com.patryk.mathdoku.solver.CageSolver;
 import com.patryk.mathdoku.gui.GameUI;
 import com.patryk.mathdoku.gui.ManualGameInputDialog;
+import com.patryk.mathdoku.gui.RandGameDialog;
+import com.patryk.mathdoku.randomGame.RandomGame;
+import com.patryk.mathdoku.solver.Solver;
 import javafx.application.Application;
 import javafx.application.Platform;
 import javafx.event.ActionEvent;
@@ -16,7 +22,9 @@ import java.io.File;
 import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.Paths;
+import java.util.List;
 import java.util.Optional;
+import java.util.Random;
 
 public class MathDoku extends Application {
     GameUI gameUI;
@@ -65,6 +73,29 @@ public class MathDoku extends Application {
 
     };
 
+    EventHandler<ActionEvent> onRanGameButtonPressed = (event) -> {
+        RandGameDialog dialog = new RandGameDialog();
+
+        Optional<RandGameDialog.RanGameInfo> result = dialog.showAndWait();
+
+        if (result.isPresent()) {
+            int seed;
+            seed = result.get().seed;
+            int size = result.get().width;
+
+            CageData cageData = RandomGame.randomGame(seed, size);
+            setGameContext(new GameContext(cageData));
+        }
+
+
+    };
+
+    private void onSolveButtonPressed(ActionEvent event) {
+        gameContext.userData.clear();
+        gameUI.wonBefore = true;
+        Solver.solve(gameContext.getUserData(), gameContext.getCageData());
+    }
+
 
 
     //key event handler
@@ -124,6 +155,8 @@ public class MathDoku extends Application {
         gameUI.checkButton.addEventHandler(ActionEvent.ANY,(event) ->  gameUI.gameGridView.showErrors());
         gameUI.fileLoadButton.addEventHandler(ActionEvent.ANY, onFileLoadButtonPressed);
         gameUI.textLoadButton.addEventHandler(ActionEvent.ANY, onManualInputButtonPressed);
+        gameUI.ranGameButton.addEventHandler(ActionEvent.ANY, onRanGameButtonPressed);
+        gameUI.solveButton.addEventHandler(ActionEvent.ANY, this::onSolveButtonPressed);
 
         gameUI.setNumberButtonCallback((digit) -> gameUI.gameGridView.getInputHandler().injectNumberKey(digit));
 
@@ -136,13 +169,15 @@ public class MathDoku extends Application {
     }
 
     public boolean wantsToExit() {
-        if (shouldDisplayExitDialog()) {
-            //show confirmation dialog
-
-
-            return gameUI.showConfirmExitDialog();
-        }
+        //todo debug
         return true;
+//        if (shouldDisplayExitDialog()) {
+//            //show confirmation dialog
+//
+//
+//            return gameUI.showConfirmExitDialog();
+//        }
+//        return true;
     }
 
     public boolean shouldDisplayExitDialog() {
diff --git a/src/com/patryk/mathdoku/UserData.java b/src/com/patryk/mathdoku/UserData.java
index 608db4927af86b573d9cb0e4d6f8b0989610f91c..55363ff16ee024bb477a6ba382190ca82eb2ab96 100755
--- a/src/com/patryk/mathdoku/UserData.java
+++ b/src/com/patryk/mathdoku/UserData.java
@@ -5,9 +5,7 @@ import com.patryk.mathdoku.util.BoardPosVec;
 import java.io.BufferedWriter;
 import java.io.FileWriter;
 import java.io.IOException;
-import java.util.Arrays;
-import java.util.LinkedList;
-import java.util.List;
+import java.util.*;
 
 public class UserData {
     public interface ChangeListener {
@@ -98,6 +96,10 @@ public class UserData {
         notifyListener(new ChangeListener.MultipleCellChange(false));
     }
 
+    public int getBoardWidth() {
+        return boardWidth;
+    }
+
     public int getPopulationCount() {
         return populationCount;
     }
@@ -122,17 +124,20 @@ public class UserData {
 
 
     public void setValueAtCell(BoardPosVec cell, int value) {
-        int oldValue = data[cell.toIndex()];
+        setValueAtCell(cell.toIndex(), value);
+    }
+    public void setValueAtCell(int index, int value) {
+        int oldValue = data[index];
 
         if (oldValue != value) {
-            data[cell.toIndex()] = value;
+            data[index] = value;
             if (oldValue == 0)
                 populationCount++;
 
             if (value == 0) {
                 populationCount--;
             }
-            notifyListener(new ChangeListener.SingleCellChange(cell, oldValue, value));
+            notifyListener(new ChangeListener.SingleCellChange(new BoardPosVec(index), oldValue, value));
 
         }
     }
@@ -167,6 +172,28 @@ public class UserData {
         return builder.toString();
     }
 
+    public Set<Integer> getAllowedDigits(int index) {
+        BoardPosVec posVec = new BoardPosVec(index);
+        int r = posVec.r;
+        int c = posVec.c;
+
+        Set<Integer> allDigits = new HashSet<>();
+        for(int i = 1; i <= boardWidth; i++) {
+            allDigits.add(i);
+        }
+
+
+        //remove row digits
+        for (int vcol = 0; vcol < c; vcol++) {
+            allDigits.remove(Integer.valueOf(getValueAtCell(new BoardPosVec(r, vcol))));
+        }
+        //remove column digits
+        for (int vrow = 0; vrow < r; vrow++) {
+            allDigits.remove(Integer.valueOf(getValueAtCell(new BoardPosVec(vrow, c))));
+        }
+        return allDigits;
+    }
+
     public void fill() {
         for (int i = 0; i < fullSize;i ++) {
             int val = (int)( 5 * Math.random()) + 1;
diff --git a/src/com/patryk/mathdoku/cageData/Cage.java b/src/com/patryk/mathdoku/cageData/Cage.java
index 147398d2ec01c4393faf9189c396d6cd52889c7e..f865fc8bbf7cd75b525948b9c4600323b50b975e 100755
--- a/src/com/patryk/mathdoku/cageData/Cage.java
+++ b/src/com/patryk/mathdoku/cageData/Cage.java
@@ -1,12 +1,15 @@
 package com.patryk.mathdoku.cageData;
 
+import java.util.ArrayList;
 import java.util.List;
 
 public class Cage {
     int target;
-    Cage.Operator operator;
-    int markedCell;
-    private final List<Integer> memberCells;
+    Cage.Operator operator = Operator.ADD;
+    int markedCell = 0;
+    private List<Integer> memberCells = new ArrayList<>();
+
+    public Cage() {}
 
     public Cage(int target, Cage.Operator operator, int markedCell, List<Integer> memberCells) {
         this.target = target;
@@ -39,6 +42,18 @@ public class Cage {
         return s;
     }
 
+    public void setTarget(int target) {
+        this.target = target;
+    }
+
+    public void setOperator(Operator operator) {
+        this.operator = operator;
+    }
+
+    public void setMemberCells(List<Integer> memberCells) {
+        this.memberCells = memberCells;
+    }
+
     public enum Operator {
         ADD('+'), SUBTRACT('-'), MULTIPLY('x'), DIVIDE('รท');
 
@@ -72,7 +87,7 @@ public class Cage {
                 case MULTIPLY:
                     return operand1 * operand2;
                 case DIVIDE:
-                    return Math.floorDiv(operand1, operand2);
+                    return Math.max(1, Math.floorDiv(operand1, operand2));
                 default:
                     return -1;
             }
diff --git a/src/com/patryk/mathdoku/cageData/CageData.java b/src/com/patryk/mathdoku/cageData/CageData.java
index 58c8115f6bffa96e7d6322dbf1b49835561d9e63..9eba4128fb50724fad2be2f67fac846b8af4bbc9 100755
--- a/src/com/patryk/mathdoku/cageData/CageData.java
+++ b/src/com/patryk/mathdoku/cageData/CageData.java
@@ -8,18 +8,34 @@ import java.util.*;
 
 public class CageData {
     int width;
-
     Cell[] data;
+
+    public List<Cage> getCageList() {
+        return cageList;
+    }
+
     List<Cage> cageList = new ArrayList<Cage>();
     int cageCount;
 
+    public CageData(int width) {
+        this.width = width;
+        data = new Cell[width * width];
+        for (int i = 0; i < width * width; i++) {
+            data[i] = new Cell(-1);
+        }
+        this.cageList = new ArrayList<>();
+
+    }
     public CageData(String data) throws DataFormatException{
         parseData(data);
     }
 
-
+    public Cell[] getData() {
+        return data;
+    }
     public List<Cage> getCages() {return cageList; }
 
+
     public boolean cellConnectsTo(BoardPosVec pos, Direction direction) {
 
         Cell nextCell = getCellAt(pos.add(direction.vector));
@@ -45,7 +61,7 @@ public class CageData {
         if (size > 81)
             throw new DataFormatException(-1, "Too many cells! Max is 81.");
         initData();
-        cageCount = parser.parseData(new Scanner(rawData), data, cageList);
+        parser.parseData(new Scanner(rawData), data, cageList);
 
     }
 
@@ -71,17 +87,11 @@ public class CageData {
     }
 
     public int getCageCount() {
-        return cageCount;
+        return cageList.size();
     }
 
-}
 
-class Cell {
-    private int cageID;
-
-    public Cell (int cageID) {
-        this.cageID = cageID;
+    public void setCageList(List<Cage> cageList) {
+        this.cageList = cageList;
     }
-
-    public int getCageId() {return cageID; }
-}
\ No newline at end of file
+}
diff --git a/src/com/patryk/mathdoku/cageData/Cell.java b/src/com/patryk/mathdoku/cageData/Cell.java
new file mode 100644
index 0000000000000000000000000000000000000000..ee915fc8189dff567b7378ef14283544bc70bd11
--- /dev/null
+++ b/src/com/patryk/mathdoku/cageData/Cell.java
@@ -0,0 +1,17 @@
+package com.patryk.mathdoku.cageData;
+
+public class Cell {
+    private int cageID;
+
+    public Cell(int cageID) {
+        this.cageID = cageID;
+    }
+
+    public int getCageId() {
+        return cageID;
+    }
+
+    public void setCageId(int cageID) {
+        this.cageID = cageID;
+    }
+}
diff --git a/src/com/patryk/mathdoku/errorChecking/CageInfo.java b/src/com/patryk/mathdoku/errorChecking/CageInfo.java
index 6e9dab6a1a58f32a8c867fe223cd38434b4198ed..82db13529e2e037ca171fceee8c8c3163066cc2e 100755
--- a/src/com/patryk/mathdoku/errorChecking/CageInfo.java
+++ b/src/com/patryk/mathdoku/errorChecking/CageInfo.java
@@ -45,7 +45,7 @@ public class CageInfo {
         populationCount++;
         if (isFull()) {
             int[] cageMemberData = getMembersOfCage();
-            isInvalid = !RecursiveSolver.testSign(cageMemberData, cage.getTarget(), cage.getOperator());
+            isInvalid = !RecursiveChecker.testSign(cageMemberData, cage.getTarget(), cage.getOperator());
         }
     }
 
diff --git a/src/com/patryk/mathdoku/errorChecking/RecursiveSolver.java b/src/com/patryk/mathdoku/errorChecking/RecursiveChecker.java
similarity index 76%
rename from src/com/patryk/mathdoku/errorChecking/RecursiveSolver.java
rename to src/com/patryk/mathdoku/errorChecking/RecursiveChecker.java
index 478fa938cb8f8eb44af57a346743e8dc559153b4..d9150ab73a4c490cfcfc8b526f7fbc9f392388d5 100755
--- a/src/com/patryk/mathdoku/errorChecking/RecursiveSolver.java
+++ b/src/com/patryk/mathdoku/errorChecking/RecursiveChecker.java
@@ -1,8 +1,11 @@
 package com.patryk.mathdoku.errorChecking;
 
+import com.patryk.mathdoku.ArrayConversions;
 import com.patryk.mathdoku.cageData.Cage;
 
-public class RecursiveSolver {
+import java.util.List;
+
+public class RecursiveChecker {
 
 
 
@@ -35,14 +38,18 @@ public class RecursiveSolver {
         return false;
     }
 
-    private RecursiveSolver(int target, Cage.Operator operator) {
+    private RecursiveChecker(int target, Cage.Operator operator) {
         this.target = target;
         this.operator = operator;
         this.permute = (operator == Cage.Operator.SUBTRACT || operator == Cage.Operator.DIVIDE);
     }
 
+
+    public static boolean testSign(List<Integer> cageList, int target, Cage.Operator operator) {
+        return testSign(ArrayConversions.toNative(cageList.toArray()), target, operator);
+    }
     public static boolean testSign(int[] cageList, int target, Cage.Operator operator) {
-        return new RecursiveSolver(target, operator).f(0, cageList, 0, 0);
+        return new RecursiveChecker(target, operator).f(0, cageList, 0, 0);
     }
 
 }
\ No newline at end of file
diff --git a/src/com/patryk/mathdoku/gui/GameGridView.java b/src/com/patryk/mathdoku/gui/GameGridView.java
index 4caa5ab8a78f00ad9289480396284041aa3cddc7..e8456803edc2c26fa40c42a1d3be90b7754715d2 100755
--- a/src/com/patryk/mathdoku/gui/GameGridView.java
+++ b/src/com/patryk/mathdoku/gui/GameGridView.java
@@ -44,7 +44,7 @@ public class GameGridView {
         }
     };
 
-    UserData.ChangeListener redrawGame = (data) -> {
+    public void redrawGame() {
         userDataDrawer.draw();
         errorHighlighter.clearCanvas();
     };
@@ -88,7 +88,7 @@ public class GameGridView {
         this.gameContext = gameContext;
         //Drawer.init(gameContext.getBoardWidth(), pixelWidth);
 
-        gameContext.getUserData().addChangeListener(redrawGame);
+        gameContext.getUserData().addChangeListener(data -> redrawGame());
 
         //link cage drawer to canvas and draw the cages
 
diff --git a/src/com/patryk/mathdoku/gui/GameUI.java b/src/com/patryk/mathdoku/gui/GameUI.java
index 4ca2379125d96e617af62644d6f4416a3e21b104..4ea4f01817e62418edd9b7dd24c9e203a1a62273 100755
--- a/src/com/patryk/mathdoku/gui/GameUI.java
+++ b/src/com/patryk/mathdoku/gui/GameUI.java
@@ -26,7 +26,7 @@ public class GameUI {
     private static final int MAX_WIDTH = 8;
     private Scene scene;
     GameContext gameContext;
-    private boolean wonBefore = false;
+    public boolean wonBefore = false;
 
     public boolean hasWonBefore() {
         return wonBefore;
@@ -38,8 +38,10 @@ public class GameUI {
     public Button redoButton = new Button("Redo");
     public Button clearButton = new Button("Clear");
     public Button checkButton = new Button("Check");
+    public Button solveButton = new Button("Solve");
     public Button fileLoadButton = new Button("Load game from file");
     public Button textLoadButton = new Button("Load game from text input");
+    public Button ranGameButton = new Button("Random game");
 
     public ComboBox<GameGridView.FontSize> fontSizeComboBox;
     GameGridView.FontSize defaultFontSize = GameGridView.FontSize.MEDIUM;
@@ -81,7 +83,7 @@ public class GameUI {
         undoRedoButtonManager = new UndoRedoButtonManager(undoButton, redoButton);
         //initialize control pane
         controlPane = new VBox();
-        controlPane.getChildren().addAll(undoButton, redoButton, clearButton, checkButton, fileLoadButton, textLoadButton);
+        controlPane.getChildren().addAll(undoButton, redoButton, clearButton, checkButton, solveButton, fileLoadButton, textLoadButton, ranGameButton);
 
         //disable clear and check buttons
         clearButton.setDisable(true);
diff --git a/src/com/patryk/mathdoku/gui/drawers/CageDrawer.java b/src/com/patryk/mathdoku/gui/drawers/CageDrawer.java
index 2d99d6d158abf6d8eb1d0b0eadf49cc568be5705..9a501567b14ae89280046ca4781b721012ba4921 100755
--- a/src/com/patryk/mathdoku/gui/drawers/CageDrawer.java
+++ b/src/com/patryk/mathdoku/gui/drawers/CageDrawer.java
@@ -90,7 +90,7 @@ public class CageDrawer extends Drawer{
 
 
         for (Cage c: data.getCages()) {
-            drawCageText(new BoardPosVec(c.getMarkedCell()), c.toString());
+            drawCageText(new BoardPosVec(c.getMemberCells().get(0)), c.toString());
         }
     }
 
diff --git a/src/com/patryk/mathdoku/randomGame/FloodFunc.java b/src/com/patryk/mathdoku/randomGame/FloodFunc.java
new file mode 100644
index 0000000000000000000000000000000000000000..f30adfe3a27b4a71b9d99f08e151c9d210c63e28
--- /dev/null
+++ b/src/com/patryk/mathdoku/randomGame/FloodFunc.java
@@ -0,0 +1,54 @@
+package com.patryk.mathdoku.randomGame;
+
+import com.patryk.mathdoku.cageData.Cell;
+import com.patryk.mathdoku.util.BoardPosVec;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+class FloodFunc {
+    Cell[] grid;
+    int maxCount;
+    int id;
+    Random ranObj;
+
+    List<Integer> members = new ArrayList();
+
+    public FloodFunc(Cell[] grid, int maxCount, int id, Random ranObj) {
+        this.grid = grid;
+        this.maxCount = maxCount;
+        this.id = id;
+        this.ranObj = ranObj;
+    }
+
+    public static List<Integer> doit(Cell[] grid, BoardPosVec pos, int maxCount, int id, Random ranObj) {
+        var obj = new FloodFunc(grid, maxCount, id, ranObj);
+        obj.rec(pos, pos);
+        return obj.members;
+    }
+
+    public void rec(BoardPosVec pos, BoardPosVec oldPos) {
+        //if on cell with other id, return
+        //place mark
+        //decrement count
+        //while you can still colour more fields, for every block around you in random order that you did not come from, recurse
+
+        if (grid[pos.toIndex()].getCageId() != -1) {
+            return;
+        }
+
+        grid[pos.toIndex()].setCageId(id);
+        members.add(pos.toIndex());
+
+        maxCount--;
+
+        BoardPosVec.forEveryCellAround(pos, ranObj, varPos -> {
+            if (maxCount > 0) {
+                if (varPos.equals(oldPos)) return;
+                rec(varPos, pos);
+            }
+        });
+
+    }
+}
diff --git a/src/com/patryk/mathdoku/randomGame/RandomCalcTree.java b/src/com/patryk/mathdoku/randomGame/RandomCalcTree.java
new file mode 100644
index 0000000000000000000000000000000000000000..a9f19ea41914c9555594c61ca9f3a6d7fc2902ea
--- /dev/null
+++ b/src/com/patryk/mathdoku/randomGame/RandomCalcTree.java
@@ -0,0 +1,35 @@
+package com.patryk.mathdoku.randomGame;
+
+import com.patryk.mathdoku.cageData.Cage;
+
+import java.util.List;
+import java.util.Random;
+
+class RandomCalcTree {
+    List<Integer> values;
+    Random ranObj;
+    Cage.Operator operator;
+
+    public RandomCalcTree(List<Integer> values, Random ranObj, Cage.Operator operator) {
+        this.values = values;
+        this.ranObj = ranObj;
+        this.operator = operator;
+        assert !values.contains(0);
+//        if (operator == Cage.Operator.DIVIDE) {
+//            while(values.remove(Integer.valueOf(0))) {}
+//        }
+    }
+
+    public static int doit(List<Integer> values, Cage.Operator operator, Random ranObj) {
+        return new RandomCalcTree(values, ranObj, operator).rec(0, values.size());
+    }
+
+    public int rec(int start, int end) {
+        if (end - start <= 1) {
+            assert values.get(start) != 0; //TODO dbg
+            return values.get(start);
+        }
+        int split = ranObj.nextInt(start + 1, end);
+        return operator.perform(rec(start, split), rec(split, end));
+    }
+}
diff --git a/src/com/patryk/mathdoku/randomGame/RandomGame.java b/src/com/patryk/mathdoku/randomGame/RandomGame.java
new file mode 100644
index 0000000000000000000000000000000000000000..a9c9c5bc13b23fc34f90f31b30dca870b190ca15
--- /dev/null
+++ b/src/com/patryk/mathdoku/randomGame/RandomGame.java
@@ -0,0 +1,63 @@
+package com.patryk.mathdoku.randomGame;
+
+import com.patryk.mathdoku.UserData;
+import com.patryk.mathdoku.cageData.Cage;
+import com.patryk.mathdoku.cageData.CageData;
+import com.patryk.mathdoku.cageData.Cell;
+import com.patryk.mathdoku.util.BoardPosVec;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+import java.util.function.Function;
+import java.util.stream.Stream;
+
+public class RandomGame {
+    static final int MAX_CAGE_SIZE = 4;
+    public static UserData userData; //todo dbg
+    public static CageData randomGame(int seed, int size) {
+        /**
+         * random array
+         * partition into cages
+         * determine operators/operands
+         */
+        BoardPosVec.setBoardWidth(size);
+        Random ranObj = new Random(seed);
+        UserData ud = randomUserData(ranObj, size);
+        CageData cd = randomCageData(ranObj, size, ud);
+        userData = ud;
+        return cd;
+    }
+
+    public static UserData randomUserData(Random ranObj, int size) {
+        return RandomUserData.doit(size, ranObj);
+    }
+
+    public static CageData randomCageData (Random ranObj, int width, UserData ud) {
+        //create blank cagedata
+        CageData cd = new CageData(width);
+        //determine cage boundaries
+        List<Cage> cl = RandomPartitionGrid.doit(cd.getData(), MAX_CAGE_SIZE, ranObj);
+        cd.setCageList(cl);
+
+
+        doOperatorsTargets(cd, ud, ranObj);
+        return cd;
+    }
+
+    public static void doOperatorsTargets(CageData cd, UserData ud, Random ranObj) {
+
+        //determine operators and operands
+        for(Cage cage: cd.getCages()) {
+            Cage.Operator op = Cage.Operator.values()[ranObj.nextInt(4)];
+            //if (op == Cage.Operator.DIVIDE) op = Cage.Operator.ADD;
+            Function<Integer, Integer>  f = (Integer cageMember )-> ud.getValueAtCell(Integer.valueOf(cageMember));
+            List<Integer> cageValues = new ArrayList(cage.getMemberCells().stream().map(f).toList());
+            int target = RandomCalcTree.doit(cageValues, op, ranObj);
+            cage.setOperator(op);
+            cage.setTarget(target);
+        }
+    }
+
+}
+
diff --git a/src/com/patryk/mathdoku/randomGame/RandomPartitionGrid.java b/src/com/patryk/mathdoku/randomGame/RandomPartitionGrid.java
new file mode 100644
index 0000000000000000000000000000000000000000..2b62dfb2d31c53534789b6b37f4ef7525006ac2a
--- /dev/null
+++ b/src/com/patryk/mathdoku/randomGame/RandomPartitionGrid.java
@@ -0,0 +1,47 @@
+package com.patryk.mathdoku.randomGame;
+
+import com.patryk.mathdoku.cageData.Cage;
+import com.patryk.mathdoku.cageData.Cell;
+import com.patryk.mathdoku.util.BoardPosVec;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+class RandomPartitionGrid {
+    final Cell[] grid;
+    final int maxPartitionSize;
+    final Random ranObj;
+
+    int cageId = 0;
+    List<Cage> cageList = new ArrayList<>();
+
+    public static List<Cage> doit(Cell[] grid, int maxPartitionSize, Random ranObj) {
+        var obj = new RandomPartitionGrid(grid, maxPartitionSize, ranObj);
+        obj.rec(new BoardPosVec(0, 0));
+        return obj.cageList;
+    }
+
+    public RandomPartitionGrid(Cell[] grid, int maxPartitionSize, Random ranObj) {
+        this.grid = grid;
+        this.maxPartitionSize = maxPartitionSize;
+        this.ranObj = ranObj;
+    }
+
+
+    private void rec(BoardPosVec pos) {
+        Cage cage = new Cage();
+        cage.setMemberCells(FloodFunc.doit(grid, pos, maxPartitionSize, cageId, ranObj));
+        cageList.add(cage);
+        cageId++;
+        for (int cellId : cage.getMemberCells()) {
+            BoardPosVec.forEveryCellAround(new BoardPosVec(cellId), (BoardPosVec varPos) -> {
+                if (grid[varPos.toIndex()].getCageId() == -1) {
+                    rec(varPos);
+                }
+            });
+        }
+    }
+
+
+}
diff --git a/src/com/patryk/mathdoku/randomGame/RandomUserData.java b/src/com/patryk/mathdoku/randomGame/RandomUserData.java
new file mode 100644
index 0000000000000000000000000000000000000000..df182859130b444837d49f21a0c89238e1b63681
--- /dev/null
+++ b/src/com/patryk/mathdoku/randomGame/RandomUserData.java
@@ -0,0 +1,56 @@
+package com.patryk.mathdoku.randomGame;
+
+import com.patryk.mathdoku.UserData;
+import com.patryk.mathdoku.util.BoardPosVec;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Random;
+
+class RandomUserData {
+    UserData userData;
+    int size;
+    Random ranObj;
+
+    public RandomUserData(int size, Random ranObj) {
+        this.size = size;
+        this.userData = new UserData(size);
+        this.ranObj = ranObj;
+    }
+
+    public static UserData doit(int size, Random ranObj) {
+        var obj = new RandomUserData(size, ranObj);
+        obj.rec(new BoardPosVec(0,0));
+        return obj.userData;
+    }
+
+    private boolean rec(BoardPosVec pos) {
+        int r = pos.r;
+        int c = pos.c;
+        //todo simplify!
+        List<Integer> allDigits = new ArrayList<>();
+        for(int i = 1; i <= size; i++) {
+            allDigits.add(i);
+        }
+
+        Collections.shuffle(allDigits, ranObj);
+
+        //remove row digits
+        for (int vcol = 0; vcol < c; vcol++) {
+            allDigits.remove(Integer.valueOf(userData.getValueAtCell(new BoardPosVec(r, vcol))));
+        }
+        //remove column digits
+        for (int vrow = 0; vrow < r; vrow++) {
+            allDigits.remove(Integer.valueOf(userData.getValueAtCell(new BoardPosVec(vrow, c))));
+        }
+
+
+        for (int digit: allDigits) {
+            userData.setValueAtCell(pos, digit);
+            if (r == size - 1 && c == size - 1 || rec(pos.next())) return true;
+        }
+
+        return false;
+    }
+}
diff --git a/src/com/patryk/mathdoku/solver/CageSolver.java b/src/com/patryk/mathdoku/solver/CageSolver.java
new file mode 100644
index 0000000000000000000000000000000000000000..4dc7ca3f75c85419ea1d1ee5cb8a6926f5200fda
--- /dev/null
+++ b/src/com/patryk/mathdoku/solver/CageSolver.java
@@ -0,0 +1,95 @@
+package com.patryk.mathdoku.solver;
+
+import com.patryk.mathdoku.GameContext;
+import com.patryk.mathdoku.UserData;
+import com.patryk.mathdoku.cageData.Cage;
+import com.patryk.mathdoku.errorChecking.RecursiveChecker;
+import com.patryk.mathdoku.util.BoardPosVec;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+public class CageSolver {
+
+    public static Set<List<Integer>> doit(UserData userData, Cage cage) {
+        Set<List<Integer>> solutions = new HashSet<>();
+        switch (cage.getOperator()) {
+            case ADD -> rec_add(userData, cage.getMemberCells(), solutions, 0, cage.getTarget());
+            case MULTIPLY -> rec_multiply(userData, solutions, cage.getMemberCells(), 0, cage.getTarget());
+            default -> rec_generic(userData, solutions, cage, 0);
+        }
+        return solutions;
+    }
+
+
+
+    private static void rec_add(UserData userData, List<Integer> cells, Set<List<Integer>> solutions, int cageCellIndex, int remaining) {
+        int boardCellIndex = cells.get(cageCellIndex);
+        Set<Integer> allowedDigits = userData.getAllowedDigits(boardCellIndex);
+        if (cageCellIndex == cells.size() - 1) {
+            if (allowedDigits.contains(remaining)) {
+                userData.setValueAtCell(boardCellIndex, remaining);
+                solutions.add(cells.stream().map(userData::getValueAtCell).toList());
+                userData.setValueAtCell(boardCellIndex, 0);
+            }
+            return;
+        }
+
+        int upperLimit = Math.min(remaining - (cells.size() - cageCellIndex - 1), BoardPosVec.getBoardWidth());
+        for(int i = upperLimit; i > 0; i--) {
+            if (allowedDigits.contains(i)) {
+                userData.setValueAtCell(boardCellIndex, i);
+                rec_add(userData, cells, solutions, cageCellIndex + 1, remaining - i);
+
+            }
+        }
+
+        userData.setValueAtCell(boardCellIndex, 0);
+    }
+
+    private static void rec_multiply(UserData userData, Set<List<Integer>> solutions, List<Integer> cells, int cageCellIndex, int remaining) {
+        int boardCellIndex = cells.get(cageCellIndex);
+        Set<Integer> allowedDigits = userData.getAllowedDigits(boardCellIndex);
+        if (cageCellIndex == cells.size() - 1) {
+            if (allowedDigits.contains(remaining)) {
+                userData.setValueAtCell(boardCellIndex, remaining);
+                solutions.add(cells.stream().map(userData::getValueAtCell).toList());
+                userData.setValueAtCell(boardCellIndex, 0);
+            }
+            return;
+        }
+
+        for (int n : allowedDigits) {
+            if (remaining % n!= 0) continue;
+
+            userData.setValueAtCell(boardCellIndex, n);
+            rec_multiply(userData, solutions, cells, cageCellIndex + 1, Math.floorDiv(remaining, n));
+        }
+        userData.setValueAtCell(boardCellIndex, 0);
+    }
+
+
+    private static void rec_generic(UserData userData, Set<List<Integer>> solutions, Cage cage, int cageCellIndex) {
+        final List<Integer> cells = cage.getMemberCells();
+        int boardCellIndex = cells.get(cageCellIndex);
+        Set<Integer> allowedDigits = userData.getAllowedDigits(boardCellIndex);
+
+        for (int n : allowedDigits) {
+            userData.setValueAtCell(boardCellIndex, n);
+
+            if (cageCellIndex == cells.size() - 1) {
+                var cageValues = cells.stream().map(userData::getValueAtCell).toList();
+                if (RecursiveChecker.testSign(cageValues, cage.getTarget(), cage.getOperator())) {
+                    solutions.add(cageValues);
+                    return;
+                }
+                userData.setValueAtCell(boardCellIndex, 0);
+                continue;
+            }
+            
+            rec_generic(userData, solutions, cage, cageCellIndex + 1);
+        }
+        userData.setValueAtCell(boardCellIndex, 0);
+    }
+}
diff --git a/src/com/patryk/mathdoku/solver/Solver.java b/src/com/patryk/mathdoku/solver/Solver.java
new file mode 100644
index 0000000000000000000000000000000000000000..281d6b461a2650f2f9eeacf95a8ce50729d91ed7
--- /dev/null
+++ b/src/com/patryk/mathdoku/solver/Solver.java
@@ -0,0 +1,75 @@
+package com.patryk.mathdoku.solver;
+
+import com.patryk.mathdoku.UserData;
+import com.patryk.mathdoku.cageData.Cage;
+import com.patryk.mathdoku.cageData.CageData;
+
+import java.util.List;
+import java.util.Set;
+
+//todo check whole
+public class Solver {
+    UserData userData;
+    List<Cage> cageList;
+
+    public Solver(UserData userData, List<Cage> cageList) {
+        this.userData = userData;
+        this.cageList = cageList;
+    }
+
+
+    public static boolean solve(UserData userData, CageData cageData) {
+        var obj = new Solver(userData, cageData.getCageList());
+        return obj.rec(0);
+    }
+
+    private boolean rec(int cageIndex) {
+        //if (cageIndex == 12) return true;
+        Cage c = cageList.get(cageIndex);
+        Set<List<Integer>> cageSolutions = CageSolver.doit(userData, c);
+        if (cageSolutions.isEmpty()) {
+            return false;
+        }
+        if (cageIndex == cageList.size() - 1) {
+            applyCageSolution(userData, c.getMemberCells(), (List<Integer>) cageSolutions.toArray()[0]);
+            return true;
+        }
+
+        for(List<Integer> solution: cageSolutions) {
+            applyCageSolution(userData, c.getMemberCells(), solution);
+            if (rec(cageIndex + 1)) return true;
+        }
+
+        clearCageSolution(userData, c.getMemberCells());
+        return false;
+    }
+
+    public static void clearCageSolution(UserData userData, List<Integer> cells) {
+
+        for(int i = 0; i < cells.size(); i++) {
+            userData.setValueAtCell(cells.get(i), 0);
+        }
+    }
+
+    public static void applyCageSolution(UserData userData, List<Integer> cells, List<Integer> cageSolution) {
+        for(int i = 0; i < cells.size(); i++) {
+            userData.setValueAtCell(cells.get(i), cageSolution.get(i));
+        }
+    }
+
+}
+
+/* rec(cageIndex) {
+    cage = cages[cageIndex]
+    solutions = get valid cage solutions
+    if solutions empty:
+        return false
+    if on last cage:
+        return first of solutions
+
+    for solution in solutions:
+        if rec(cageIndex + 1) return true
+
+    return false
+
+ */
\ No newline at end of file
diff --git a/src/com/patryk/mathdoku/solver/pseudocode.txt b/src/com/patryk/mathdoku/solver/pseudocode.txt
new file mode 100644
index 0000000000000000000000000000000000000000..3ba552b308722b13c43441308173439e74fa93ba
--- /dev/null
+++ b/src/com/patryk/mathdoku/solver/pseudocode.txt
@@ -0,0 +1,42 @@
+add:
+proc rec(cell of cage, sum#remaining):
+    if on last cell and remaining not in forbidden digits:
+        write remaining into cell
+        return true
+
+    upper limit = remaining -  number of blanks in cage + 1
+    for n in reverse(range(1, min(upper limit, N)):
+        if n not in forbidden digits:
+            write N into cell
+            if recurse(next cell in the cage) is true:
+                return true
+
+    blank your spot
+    return false
+
+multiply:
+proc rec(cell index, remaining):
+    if on last cell and remaining not in forbidden digits:
+        write remaining into cell
+        return true
+
+    for every factor n of remaining less than limit:
+        if n not in forbidden digits:
+            write N into cell
+            if recurse(cell index + 1, remaining / factor) is true:
+                return true
+
+    blank your spot
+    return false
+
+generic:
+proc rec:
+
+    for every number up to limit that is not in forbidden digits:
+        write N into cell
+        if on last cell:
+            record solution
+            return true
+        if recurse(cell index + 1) return true
+
+    return false
diff --git a/src/com/patryk/mathdoku/util/BoardPosVec.java b/src/com/patryk/mathdoku/util/BoardPosVec.java
index c0375835154c11c756d64eaa3b07f826ada25fba..3fa4bf172605c611e955dc62d23d3d1a9b64fbb8 100755
--- a/src/com/patryk/mathdoku/util/BoardPosVec.java
+++ b/src/com/patryk/mathdoku/util/BoardPosVec.java
@@ -1,5 +1,8 @@
 package com.patryk.mathdoku.util;
 
+import java.util.*;
+import java.util.function.Consumer;
+
 public class BoardPosVec {
     //private static GameContext gameContext = GameContext.getInstance();
     //public static int width = GameContext.getBoardWidth();
@@ -32,6 +35,10 @@ public class BoardPosVec {
         c = index - r * boardWidth;
     }
 
+    public static int getBoardWidth() {
+        return boardWidth;
+    }
+
     public BoardPosVec add(BoardPosVec other) {
         return new BoardPosVec(this.r + other.r, this.c + other.c);
     }
@@ -64,6 +71,11 @@ public class BoardPosVec {
                 c >= 0 && c < boardWidth;
     }
 
+    public static boolean isValid(int r, int c) {
+        return r >= 0 && r < boardWidth &&
+                c >= 0 && c < boardWidth;
+    }
+
     public int toIndex() {
         return boardWidth * r + c;
     }
@@ -77,9 +89,43 @@ public class BoardPosVec {
         return new BoardPosVec(Util.pixelToBoard(this.r, boardWidth, pixelWidth), Util.pixelToBoard(this.c, boardWidth, pixelWidth));
     }
 
+    public static void forEveryCellAround(BoardPosVec pos, Random ranObj, Consumer<BoardPosVec> func) {
+        BoardPosVec[] around = {
+                pos.add(new BoardPosVec(0,1)),
+                pos.add(new BoardPosVec(0,-1)),
+                pos.add(new BoardPosVec(1,0)),
+                pos.add(new BoardPosVec(-1,0)),
+        };
+
+        List<BoardPosVec> aroundObj = new ArrayList(List.of(around));
+        if (ranObj != null) Collections.shuffle(aroundObj, ranObj);
+
+
+        for (var vec: aroundObj) {
+            if (!vec.isValid()) continue;
+            func.accept(vec);
+        }
+    }
+
+    public static void forEveryCellAround(BoardPosVec pos, Consumer<BoardPosVec> func) {
+        forEveryCellAround(pos,null, func);
+    }
 
     @Override
     public String toString() {
         return String.format("(%d, %d)", r, c);
     }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        BoardPosVec that = (BoardPosVec) o;
+        return r == that.r && c == that.c;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(r, c);
+    }
 }