Создание копии объекта в c#

Пытаюсь реализовать метод поиска в ширину для решения 8-головоломки. Задача - найти путь решения от начального положения поля до конченого. На вход подаются 3 строки начального положения поля головоломки, например:

1 2 3
4 5 6
7 0 8

Вместо пустой ячейки на поле использую 0. Дальше эти значения записываются в виде двумерного массива в поле state объекта startState, и в нем автоматически вычисляются индексы i, j положения нуля в массиве. Также задается итоговое поле решенной головоломки (объект goalState):

1 2 3
4 5 6
7 8 0

Далее передаю эти объекты в метод doSearch - в котором реализуется поиск в ширину. В алгоритме поиска пытаюсь передать первый элемент из списка Open в метод findSuccessors. В этом методе пытаюсь скопировать переданный в метод объект node в объект node_copy с помощью конструктора клонирования объекта класса FieldState:

FieldState node_copy = new FieldState(node);

Далее мне необходимо по положению нуля в поле посмотреть, какие варианты ходов возможны (чтобы 0 не выходил за поле) и для скопированного объекта изменить положение нуля (поменять местами его с соседней ячейкой поля, если это возможно). После чего скопированный объект должен заноситься в список successors для продолжения поиска в ширину. Проблема заключается в том, что при вызове конструктора

FieldState node_copy = new FieldState(node);

Объекту node_copy присваивается ссылка на объект node, и копии объекта не создается. Пробовал реализовать интерфейс IClonable, получается то же самое. Не пойму, в чем причина. С с# до этого не работал, для интереса захотелось написать на нем, но столкнулся с такой проблемой. Буду рад, если кто-то поможет.

Полный код:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;

namespace App8Logic
{
    //Класс для создания объекта-состояния игрового поля
    public class FieldState
    {
        //Индексы положения 0 в массиве
        public int i { get; set; }
        public int j { get; set; }
        //Массив состояния игрового поля
        public int[,] state { get; set; }
        //Ссылка на предыдущее состояние поля
        public FieldState predcessor { get; set; }

        //Конструктор копирования объекта
        public FieldState(FieldState obj) {
            i = obj.i;
            j = obj.j;
            state = obj.state;
            predcessor = obj.predcessor;

        }

        //Конструктор для создания объекта по вводу состояния игрового поля
        public FieldState(int[,] array)
        {
            
            for (int i1 = 0; i1 < 3; i1++) {
                for (int j1 = 0; j1 < 3; j1++) {
                    if (array[i1, j1] == 0) {
                        i = i1;
                        j = j1;
                    }
                }
            }
            state = array;
        }

    }

    //Класс для поиска в ширину
    public class Search 
    {
        //На вход подаются конечное и начальное состояния игрового поля
        public static FieldState doSearch(FieldState startState, FieldState goalState) {
            //Создается очередь с открытыми состояниями (которые нужно проверить)
            Queue <FieldState> Open = new Queue <FieldState>();
            //Создается очередь с закрытыми состояниями (которые уже проверены)
            Queue<FieldState> Closed = new Queue<FieldState>();
            //В очередь добавляется начальное состояние поля 
            Open.Enqueue(startState);

            //Пока очередь не пуста
            while (Open.Count != 0) {
                //Достается первое состояние из очереди
                FieldState nodeX = Open.Dequeue();
                //Если это конченое состояние, возвращаем его
                if (nodeX == goalState)
                {
                    return nodeX;
                }

                //Если нет, находим возможные состояния игрового поля после 1 хода 
                List<FieldState> successors =Successors.findSuccessors(nodeX);

                //Для каждого состояния
                foreach (FieldState successor in successors) {
                    //Если оно не в списке, помещаем его туда
                    if (!Open.Contains(successor) && !Closed.Contains(successor)) {
                        Open.Enqueue(successor);
                        
                    }
                }
                //Помещаем исследованное состояние в список Closed
                Closed.Enqueue(nodeX);
                 
            }
            //Если поиск неудачен, возвращаем последнее состояние поля 
            return Closed.Last();
        }
    }

    //Класс для поиска возможных ходов игрового поля
    public class Successors {
        public static List<FieldState> findSuccessors(FieldState node)
        {

            //Создаем список для возможных ходов
            List<FieldState> successors = new List<FieldState>();

            //Проверка возможности перемещения пустой ячейки вверх
            if (node.i - 1 >= 0) {

                //Копируем объект
                FieldState node_copy = new FieldState(node);

                //Перемещаем пустую ячейку на поле
                node_copy.state[node_copy.i, node_copy.j] = node_copy.state[node_copy.i, node_copy.j-1];
                node_copy.state[node_copy.i , node_copy.j- 1] = 0;

                //Новый ход в список
                successors.Add(node_copy);

                //Удаляем копию объекта
                node_copy = null;
            }
            //Проверка возможности перемещения пустой ячйки влево
            if (node.j + 1 <= 2)
            {

                FieldState node_copy = new FieldState(node);

                node_copy.state[node_copy.i, node_copy.j] = node_copy.state[node_copy.i, node_copy.j+1];
                node_copy.state[node_copy.i, node_copy.j + 1] = 0;

                successors.Add(node_copy);

                node_copy = null;
            }
            //Проверка возможности перемещения пустой ячейки вниз
            if (node.i + 1 <= 2)
            {

                FieldState node_copy = node;

                node_copy.state[node_copy.i, node_copy.j] = node_copy.state[node_copy.i+1, node_copy.j];
                node_copy.state[node_copy.i + 1, node_copy.j] = 0;

                successors.Add(node_copy);

                node_copy = null;
            }
            //Проверка возможности перемещения пустой ячейки вправо
            if (node.j - 1 >= 0)
            {

                FieldState node_copy = node;

                node_copy.state[node_copy.i, node_copy.j] = node_copy.state[node_copy.i, node_copy.j - 1];
                node_copy.state[node_copy.i, node_copy.j - 1] = 0;

                successors.Add(node_copy);

                node_copy = null;
            }

            //Возвращаем возможные ходы
            return successors;


        }

    }
    class Program
    {
        static void Main(string[] args)
        {
            //Создаем итоговое состояние игрового поля
            int[,] goal_array = { { 1,2,3},{ 4,5,6 },{7,8,0 } };
            FieldState goalState = new FieldState(goal_array);

            //Вводим начальное состояние игрового поля
            int[,] start_array = new int[3, 3];

            Console.WriteLine("Введите начальное положение игрового поля (в 3 строки)\n");

            for (int i = 0; i <= 2; i++) {
                
                string[] input = Console.ReadLine().Split(' ');
                for (int j = 0; j <= 2; j++) {

                    start_array[i,j]= Convert.ToInt32(input[j]);
                }
            }

            Console.WriteLine("\n");

            FieldState startState = new FieldState(start_array);

            //Получаем последние состояние игрового поля после поиска
            FieldState nodeExit = Search.doSearch(startState, goalState);

            //Далее будет нужно вывести предшественников в обратном порядке
            /*
            while (nodeExit.predcessor != null) {
                for (int i = 0; i <= 2; i++)
                {

                   Console.WriteLine(nodeExit.state[i, 0]+" "+ nodeExit.state[i, 1]+" "+ nodeExit.state[i, 2]);
                    
                }
                Console.WriteLine("\n");
            }
            */

            Console.ReadKey();
        }
    }
}


Ответы (0 шт):