Skip to content
Snippets Groups Projects
Board.cs 5.42 KiB
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Reflection.Metadata;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace PuzzlePlayer_Namespace
{
    public enum SOLUTIONS
    {
        NONE = 0,
        UNIQUE = 1,
        MULTIPLE = 2
    }

    // a struct for a move on a certain location. changeTo is in what the place on the coordinates should change to
    public struct Move
    {
        public int x, y;
        public int changeTo;

        public Move(int x, int y, int changeTo)
        {
            this.x = x;
            this.y = y;
            this.changeTo = changeTo;
        }
    }

    /*
     * This abstract class can be used to implement any kind of puzzle
     * One thing that is common for all puzzles is that a empty space is equal to -1
     * GetSolveList and IsBoardValid need to be overwriten in a subclass of Board
     */
    public abstract class Board
    {
        public const int emptySpace = -1; // for every puzzle -1 represents a empty space
        public string description;
        public int drawFactor; // setting this to 1 always works

        // variables to keep track of the board state and the last generated board
        public int[,] boardState;
        public int[,] lastGeneratedBoard;
        public int[,] solution;

        // static meathode for filling a int[,] with -1
        public static int[,] GetClearBoard(int boardSizeX, int boardSizeY)
        {
            int[,] result = new int[boardSizeX, boardSizeY];
            // fill the board with empty spaces (-1)
            for (int i = 0; i < boardSizeX; i++)
            {
                for (int j = 0; j < boardSizeY; j++)
                    result[i, j] = emptySpace;
            }

            return result;
        }



        public abstract void Draw(Graphics gr, Rectangle r);

        // a methode for solving the whole board. It uses the private SolveStep methode untill the whole board is solved
        // it has one parameter. setting this to true will only give a return value without changing the current boardState
        public virtual SOLUTIONS Solve(bool CheckOnly)
        {
            // two variables for storing the result and the next solveStep
            int[,] result = (int[,])boardState.Clone();
            List<Move> possibleMoves;

            // keep looping until the SolveStep returns an empty list
            // if SolveStep returns a empty list that could mean that either the whole board is solved or it is imposible to solve the board
            while ((possibleMoves = GetSolveList(result)).Count != 0)
            {
                foreach (Move m in possibleMoves)
                {
                    result[m.x, m.y] = m.changeTo;
                }
            }
        
            // check if the whole board is filled
            // if not then the methode will return null because it is imposible to solve the board
            for (int i = 0; i < result.GetLength(0); i++)
                for (int j = 0; j < result.GetLength(1); j++)
                    if (result[i, j] == emptySpace)
                    {
                        return SOLUTIONS.NONE;
                    }

            if(!CheckOnly)
                boardState = result;
            return SOLUTIONS.UNIQUE;
        }

        // abstract methode for solving one step
        public virtual Point Hint() { return new Point(0, 0); }

        // a abstract methode to get a list of all possible moves
        protected virtual List<Move> GetSolveList(int[,] boardToSolve)
        {
            List<Move> result = new List<Move>();

            return result;
        }

        // abstract methode for generating a random board
        public abstract void Generate();

        // abstract methode for checking if a imputed boardstate is valid
        public virtual bool IsBoardSolved()
        {
            for (int i = 0; i < boardState.GetLength(0); i++) for (int j = 0;j < boardState.GetLength(1); j++)
                {
                    if (boardState[i,j] != solution[i,j]) return false;
                }
            return true;
            //MessageBox.Show($"{boardState[0, 0]},{boardState[0, 1]},{boardState[1, 0]},{boardState[1, 1]} hjjkjkhj {solution[0, 0]},{solution[0, 1]},{solution[1, 0]},{solution[1, 1]} ");
            //MessageBox.Show($"{boardState == solution}");
        }

        // changes tile P to value X
        public abstract bool TileInput(Point? p, Keys k);

        // performs a left/right (X) click on tile P, changing its value
        public virtual bool TileClick(Point p, int x) { return false; }

        // is called by restartbutton on PuzzleForm
        public virtual void Restart()
        {
            boardState = (int[,])lastGeneratedBoard.Clone();
        }
    }


    // static class for extentions
    public static class Extensions
    {
        private static readonly Random rng = new Random();

        // shuffle the elements in a list around to get different results every time from the generator algoritms
        // First answer from: https://stackoverflow.com/questions/273313/randomize-a-listt
        public static void Shuffle<T>(this IList<T> list)
        {
            int n = list.Count;
            while (n > 1)
            {
                n--;
                int k = rng.Next(n + 1);
                T value = list[k];
                list[k] = list[n];
                list[n] = value;
            }
        }
    }
}