Task: 15 puzzle game



C#: using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;

public class FifteenPuzzle
{
    const int GridSize = 4; 
    const int BlockCount = 16;

    static readonly Random R = new Random();

    private List<Button> Puzzles = new List<Button>();
    private int Moves = 0;
    private DateTime Start;

    public class Puzzle
    {
        private int mOrderedNumer;

        public int CurrentNumber;

        public int X;
        public int Y;

        public int InvX
        {
            get { return (GridSize - 1) - X; }
        }
        public int InvY
        {
            get { return (GridSize - 1) - Y; }
        }

        public Puzzle(int OrderedNumer)
        {
            mOrderedNumer = OrderedNumer;

            CurrentNumber = OrderedNumer;

            X = OrderedNumer % GridSize;
            Y = OrderedNumer / GridSize;
        }
        public Puzzle(int OrderedNumer, int CurrentNumber)
            : this(OrderedNumer)
        {
            this.CurrentNumber = CurrentNumber;
        }

        public bool IsEmptyPuzzle
        {
            get { return CurrentNumber >= (BlockCount - 1); }
        }
        public bool IsTruePlace
        {
            get { return (CurrentNumber == mOrderedNumer); }
        }
        public bool NearestWith(Puzzle OtherPz)
        {
            int dx = (X - OtherPz.X);
            int dy = (Y - OtherPz.Y);

            if ((dx == 0) && (dy <= 1) && (dy >= -1)) return true;
            if ((dy == 0) && (dx <= 1) && (dx >= -1)) return true;

            return false;
        }

        public override string ToString()
        {
            return (CurrentNumber + 1).ToString();
        }
    }

    public static void Main(string[] args)
    {
        FifteenPuzzle Game = new FifteenPuzzle();
        Application.Run(Game.CreateForm());
    }

    private Form CreateForm()
    {
        int ButtonSize = 50;
        int ButtonMargin = 3;
        int FormEdge = 9;

        Font ButtonFont = new Font("Arial", 15.75F, FontStyle.Regular);

        Button StartButton = new Button();
        StartButton.Location = new Point(FormEdge, (GridSize * (ButtonMargin + ButtonSize)) + FormEdge);
        StartButton.Size = new Size(86, 23);
        StartButton.Font = new Font("Arial", 9.75F, FontStyle.Regular);
        StartButton.Text = "New Game";
        StartButton.UseVisualStyleBackColor = true;
        StartButton.TabStop = false;

        StartButton.Click += new EventHandler(NewGame);

        int FormWidth = (GridSize * ButtonSize) + ((GridSize - 1) * ButtonMargin) + (FormEdge * 2);
        int FormHeigth = FormWidth + StartButton.Height;

        Form Form = new Form();
        Form.Text = "Fifteen";
        Form.ClientSize = new Size(FormWidth, FormHeigth);
        Form.FormBorderStyle = FormBorderStyle.FixedSingle;
        Form.MaximizeBox = false;
        Form.SuspendLayout();

        for (int i = 0; i < BlockCount; i++)
        {
            Button Bt = new Button();
            Puzzle Pz = new Puzzle(i);

            int PosX = FormEdge + (Pz.X) * (ButtonSize + ButtonMargin);
            int PosY = FormEdge + (Pz.Y) * (ButtonSize + ButtonMargin);
            Bt.Location = new Point(PosX, PosY);

            Bt.Size = new Size(ButtonSize, ButtonSize);
            Bt.Font = ButtonFont;

            Bt.Text = Pz.ToString();
            Bt.Tag = Pz;
            Bt.UseVisualStyleBackColor = true;
            Bt.TabStop = false;

            Bt.Enabled = false;
            if (Pz.IsEmptyPuzzle) Bt.Visible = false;

            Bt.Click += new EventHandler(MovePuzzle);

            Puzzles.Add(Bt);
            Form.Controls.Add(Bt);
        }

        Form.Controls.Add(StartButton);
        Form.ResumeLayout();

        return Form;
    }

    private void NewGame(object Sender, EventArgs E)
    {
        do
        {
            for (int i = 0; i < Puzzles.Count; i++)
            {
                Button Bt1 = Puzzles[R.Next(i, Puzzles.Count)];
                Button Bt2 = Puzzles[i];
                Swap(Bt1, Bt2);
            }
        }
        while (!IsSolvable());

        for (int i = 0; i < Puzzles.Count; i++)
        {
            Puzzles[i].Enabled = true;
        }

        Moves = 0;
        Start = DateTime.Now;
    }

    private void MovePuzzle(object Sender, EventArgs E)
    {
        Button Bt1 = (Button)Sender;
        Puzzle Pz1 = (Puzzle)Bt1.Tag;

        Button Bt2 = Puzzles.Find(Bt => ((Puzzle)Bt.Tag).IsEmptyPuzzle);
        Puzzle Pz2 = (Puzzle)Bt2.Tag;

        if (Pz1.NearestWith(Pz2))
        {
            Swap(Bt1, Bt2);
            Moves++;
        }

        CheckWin();
    }

    private void CheckWin()
    {
        Button WrongPuzzle = Puzzles.Find(Bt => !((Puzzle)Bt.Tag).IsTruePlace);
        bool UWin = (WrongPuzzle == null);

        if (UWin)
        {
            for (int i = 0; i < Puzzles.Count; i++)
            {
                Puzzles[i].Enabled = false;
            }

            TimeSpan Elapsed = DateTime.Now - Start;
            Elapsed = TimeSpan.FromSeconds(Math.Round(Elapsed.TotalSeconds, 0));
            MessageBox.Show(String.Format("Solved in {0} moves. Time: {1}", Moves, Elapsed));
        }
    }

    private void Swap(Button Bt1, Button Bt2)
    {
        if (Bt1 == Bt2) return;

        Puzzle Pz1 = (Puzzle)Bt1.Tag;
        Puzzle Pz2 = (Puzzle)Bt2.Tag;

        int g = Pz1.CurrentNumber;
        Pz1.CurrentNumber = Pz2.CurrentNumber;
        Pz2.CurrentNumber = g;

        Bt1.Visible = true;
        Bt1.Text = Pz1.ToString();
        if (Pz1.IsEmptyPuzzle) Bt1.Visible = false;

        Bt2.Visible = true;
        Bt2.Text = Pz2.ToString();
        if (Pz2.IsEmptyPuzzle) Bt2.Visible = false;
    }

    private bool IsSolvable()
    {
        
        

        int InvCount = 0;
        for (int i = 0; i < Puzzles.Count - 1; i++)
        {
            for (int j = i + 1; j < Puzzles.Count; j++)
            {
                Puzzle Pz1 = (Puzzle)Puzzles[i].Tag;
                if (Pz1.IsEmptyPuzzle) continue;

                Puzzle Pz2 = (Puzzle)Puzzles[j].Tag;
                if (Pz2.IsEmptyPuzzle) continue;

                if (Pz1.CurrentNumber > Pz2.CurrentNumber) InvCount++;
            }
        }

        Button EmptyBt = Puzzles.Find(Bt => ((Puzzle)Bt.Tag).IsEmptyPuzzle);
        Puzzle EmptyPz = (Puzzle)EmptyBt.Tag;

        bool Result = false;
        if ((EmptyPz.InvY + 1) % 2 == 0) 
        {
            
            if (InvCount % 2 != 0) Result = true;
        }
        else 
        {
            
            if (InvCount % 2 == 0) Result = true;
        }
        return Result;
    }
}






VB: Public iSide As Integer
Public iSize As Integer
Public iGrid() As Integer
Public lMoves As Long
Public sMessage As String
Public Const sTitle As String = "Tile Puzzle"


Sub PlayGame()
    Dim iNum As Integer
    Dim i As Integer
    Dim vInput As Variant

DefineGrid:
    vInput = InputBox("Enter size of grid, as a whole number" & String(2, vbCr) & "(e.g. for a 4 x 4 grid, enter 
    If vInput = "" Then Exit Sub
    If Not IsNumeric(vInput) Then GoTo DefineGrid
    iSide = vInput
    If iSide < 3 Or iNum > 10 Then GoTo DefineGrid
    iSize = iSide ^ 2
    ReDim iGrid(1 To iSize)
    
Initalize:
    InitializeGrid
    If Not IsSolvable Then GoTo Initalize
      
GetInput:
    vInput = InputBox(ShowGrid & vbCr & "Enter number to move into empty tile", sTitle)
    If vInput = "" Then
        If MsgBox("Are you sure? This will end the current game.", vbExclamation + vbYesNo, sTitle) = vbYes Then Exit Sub
    End If
    If Not IsNumeric(vInput) Then
        sMessage = "
        GoTo GetInput
    End If
    iNum = vInput
    If iNum < 1 Or iNum > iSize - 1 Then
        sMessage = iNum & " is not a valid tile"
        GoTo GetInput
    End If
    i = FindTile(iNum)
    If Not ValidMove(i) Then GoTo GetInput
    MoveTile (i)
    If TestGrid Then
        MsgBox "SUCCESS! You solved the puzzle in " & lMoves & " moves", vbInformation + vbOKOnly, sTitle
    Else
        GoTo GetInput
    End If
End Sub

Function RandomTile() As Integer
    Randomize
    RandomTile = Int(Rnd * iSize) + 1
End Function

Function GetX(ByVal i As Integer) As Integer
    GetX = Int((i - 1) / iSide) + 1
End Function

Function GetY(ByVal i As Integer) As Integer
    GetY = (i - 1) Mod iSide + 1
End Function

Function GetI(ByVal x As Integer, y As Integer)
    GetI = (x - 1) * iSide + y
End Function

Function InitializeGrid()
    Dim i As Integer
    Dim x As Integer
    Dim y As Integer
    
    sMessage = "New " & iSide & " x " & iSide & " game started" & vbCr
    
    For i = 1 To iSize
        iGrid(i) = 0
    Next i
    For i = 1 To iSize - 1
        Do
            x = RandomTile
            If iGrid(x) = 0 Then iGrid(x) = i
        Loop Until iGrid(x) = i
    Next i
    lMoves = 0
End Function

Function IsSolvable() As Boolean
    Dim i As Integer
    Dim j As Integer
    Dim iCount As Integer
    For i = 1 To iSize - 1
        For j = i + 1 To iSize
            If iGrid(j) < iGrid(i) And iGrid(j) > 0 Then iCount = iCount + 1
        Next j
    Next i
    If iSide Mod 2 Then
        IsSolvable = Not iCount Mod 2
    Else
        IsSolvable = iCount Mod 2 = GetX(FindTile(0)) Mod 2
    End If
End Function

Function TestGrid() As Boolean
    Dim i As Integer
    
    For i = 1 To iSize - 1
        If Not iGrid(i) = i Then
            TestGrid = False
            Exit Function
        End If
    Next i
    TestGrid = True
End Function

Function FindTile(ByVal iNum As Integer) As Integer
    Dim i As Integer
    For i = 1 To iSize
        If iGrid(i) = iNum Then
            FindTile = i
            Exit Function
        End If
    Next i
End Function

Function ValidMove(ByVal i As Integer) As Boolean
    Dim e As Integer
    Dim xDiff As Integer
    Dim yDiff As Integer
    
    e = FindTile(0)
    xDiff = GetX(i) - GetX(e)
    yDiff = GetY(i) - GetY(e)
    If xDiff = 0 Then
        If yDiff = 1 Then
            sMessage = "Tile " & iGrid(i) & " was moved left"
            ValidMove = True
        ElseIf yDiff = -1 Then
            sMessage = "Tile " & iGrid(i) & " was moved right"
            ValidMove = True
        End If
    ElseIf yDiff = 0 Then
        If xDiff = 1 Then
            sMessage = "Tile " & iGrid(i) & " was moved up"
            ValidMove = True
        ElseIf xDiff = -1 Then
            sMessage = "Tile " & iGrid(i) & " was moved down"
            ValidMove = True
        End If
    End If
    If Not ValidMove Then sMessage = "Tile " & iGrid(i) & " may not be moved"
End Function

Function MoveTile(ByVal i As Integer)
    Dim e As Integer
    e = FindTile(0)
    iGrid(e) = iGrid(i)
    iGrid(i) = 0
    lMoves = lMoves + 1
End Function

Function ShowGrid()
    Dim x As Integer
    Dim y As Integer
    Dim i As Integer
    Dim sNum As String
    Dim s As String
    
    For x = 1 To iSide
        For y = 1 To iSide
            sNum = iGrid(GetI(x, y))
            If sNum = "0" Then sNum = ""
            s = s & sNum & vbTab
        Next y
        s = s & vbCr
    Next x
    If Not sMessage = "" Then
        s = s & vbCr & sMessage & vbCr
    End If
    ShowGrid = s
End Function