public static void printaMelhorCromossomo(int geracao, Cromossomo cromossomo)
        {
            var aux_list = _list;

            foreach (var movimento in cromossomo.movimentosValidos)
            {
                aux_list[movimento.linha][movimento.coluna] = '$';
            }

            Console.WriteLine();
            Console.WriteLine($"Melhor Cromossomo da geração: {geracao} - Caminho: {printaCaminho(cromossomo)}- Aptidão: {Math.Round(cromossomo.peso, 2)}");
            foreach (var item in aux_list)
            {
                for (int i = 0; i < item.Length; i++)
                {
                    if (item[i] == '$')
                    {
                        Console.BackgroundColor = ConsoleColor.Red;
                        Console.Write($" {item[i]} ");
                        Console.ResetColor();
                    }
                    else
                    {
                        Console.Write($" {item[i]} ");
                    }
                }
                Console.WriteLine();
            }
        }
        public static string printaCaminho(Cromossomo cromossomo)
        {
            string retorno = string.Empty;

            foreach (var item in cromossomo.movimentosValidos)
            {
                retorno += $"({item.linha},{item.coluna}) ";
            }

            return(retorno);
        }
        public static string printaMovimentos(Cromossomo cromossomo)
        {
            string retorno = string.Empty;

            foreach (var item in cromossomo.movimentos)
            {
                retorno += $"{item} ";
            }

            return(retorno);
        }
        public static bool jaVisitouPosicao(Cromossomo cromossomo, int linha, int coluna)
        {
            foreach (var item in cromossomo.movimentosValidos)
            {
                if (item.linha == linha && item.coluna == coluna)
                {
                    return(true);
                }
            }

            return(false);
        }
        public static double calculaAptidao(Cromossomo cromossomo, int caminhosLivres)
        {
            //percentual referente a quantidade de movimentos contra parede pelo agente
            double movimentosEmParedes = Math.Abs((((double)(caminhosLivres - cromossomo.movimentosEmParedes) / caminhosLivres)) * 0.3);

            //percentual referente a quantidade de movimentos invalidos (fora da matriz)
            double movimentosInvalidos = Math.Abs((((double)(caminhosLivres - cromossomo.movimentosInvalidos) / caminhosLivres)) * 0.6);

            //percentual referente a quantidade de movimentos em laçoes feitos
            double MovimentosJaVisitados = Math.Abs((((double)(caminhosLivres - cromossomo.MovimentosJaVisitados) / caminhosLivres)) * 0.1);

            //percentual referente aos movimentos validos
            double MovimentosValidos = Math.Abs((((double)(caminhosLivres - cromossomo.movimentosValidos.Count) / caminhosLivres)));

            return(Math.Abs((1 - (movimentosEmParedes + movimentosInvalidos + MovimentosJaVisitados)) + (1 - MovimentosValidos)));
        }
        public static bool encontrouImpedimento(Cromossomo cromossomo, int linha, int coluna)
        {
            bool retorno = false;

            if (iraSairDaMatriz(linha, coluna))
            {
                cromossomo.movimentosInvalidos++;
                return(true);
            }

            if (_list[linha][coluna] == '1')
            {
                cromossomo.movimentosEmParedes++;
                retorno = true;
            }

            if (jaVisitouPosicao(cromossomo, linha, coluna))
            {
                cromossomo.MovimentosJaVisitados++;
            }

            return(retorno);
        }
        static void Main(string[] args)
        {
            int    populacao = 120;
            int    geracoes  = 10;
            bool   printa    = true;
            string caminho   = @"C:\Users\mathe\Desktop\IA\T1201902_arquivosDeEntrada\labirinto4_20.txt";

#if !DEBUG
            bool entrada = true;
            while (entrada)
            {
                try
                {
                    Console.WriteLine(" - Informe o caminho do arquivo de entrada.");
                    caminho = Console.ReadLine();
                    Console.WriteLine(" - Informe o tamanho da população.");
                    int.TryParse(Console.ReadLine(), out populacao);
                    Console.WriteLine(" - Informe o número de gerações");
                    int.TryParse(Console.ReadLine(), out geracoes);

                    if (!File.Exists(caminho))
                    {
                        throw new ArgumentException("Caminho não encotrado.");
                    }

                    entrada = false;
                }
                catch
                {
                    Console.Clear();
                    Console.WriteLine(" - Erro encontrado, reinsira as entradas.");
                }
            }
#endif

            leArquivoEntrada(caminho);

            List <Cromossomo> cromossomos = new List <Cromossomo>();
            int caminhosLivres            = retornaCaminhosLivres();

            printaLabirinto(printa);

            Random rand = new Random();

            bool agenteEncontrouSaida = false;
            int  count = 0;

            while (count < geracoes && !agenteEncontrouSaida)
            {
                if (count == 0) //gera população inicial
                {
                    cromossomos = geraPrimeiraGeracaoCromossomos(rand, populacao, caminhosLivres);
                }
                else //etapa de seleção
                {
                    List <Cromossomo> novaGeracao = new List <Cromossomo>();

                    //elitismo
                    var melhoresCromossomos = cromossomos.OrderByDescending(x => x.peso).ToList();

                    novaGeracao.Add(melhoresCromossomos[0]);

                    //torneio - cruzamento - mutação
                    while (novaGeracao.Count != populacao)
                    {
                        //seleção pai
                        int pai1 = 0, pai2 = 0;
                        while (pai1 == pai2)
                        {
                            pai1 = rand.Next(0, melhoresCromossomos.Count);
                            pai2 = rand.Next(0, melhoresCromossomos.Count);
                        }
                        Cromossomo pai;

                        if (melhoresCromossomos[pai1].peso > melhoresCromossomos[pai2].peso)
                        {
                            pai = melhoresCromossomos[pai1];
                        }
                        else
                        {
                            pai = melhoresCromossomos[pai2];
                        }

                        //seleção mãe
                        int mae1 = 0, mae2 = 0;
                        while (mae1 == mae2)
                        {
                            mae1 = rand.Next(0, melhoresCromossomos.Count);
                            mae2 = rand.Next(0, melhoresCromossomos.Count);
                        }
                        Cromossomo mae;

                        if (melhoresCromossomos[mae1].peso > melhoresCromossomos[mae2].peso)
                        {
                            mae = melhoresCromossomos[mae1];
                        }
                        else
                        {
                            mae = melhoresCromossomos[mae2];
                        }

                        //crossover 2 pontos
                        Cromossomo filho1 = new Cromossomo();
                        Cromossomo filho2 = new Cromossomo();

                        int metadePai = pai.movimentos.Count / 3;
                        int metadeMae = mae.movimentos.Count / 3;

                        //filho 1
                        for (int j = 0; j < metadePai; j++)
                        {
                            filho1.movimentos.Add(pai.movimentos[j]);
                        }

                        for (int j = metadeMae; j < (metadeMae * 2); j++)
                        {
                            filho1.movimentos.Add(mae.movimentos[j]);
                        }

                        for (int j = (metadePai * 2); j < pai.movimentos.Count; j++)
                        {
                            filho1.movimentos.Add(pai.movimentos[j]);
                        }

                        //filho 2
                        for (int j = 0; j < metadeMae; j++)
                        {
                            filho2.movimentos.Add(mae.movimentos[j]);
                        }

                        for (int j = metadePai; j < (metadePai * 2); j++)
                        {
                            filho2.movimentos.Add(pai.movimentos[j]);
                        }

                        for (int j = (metadeMae * 2); j < mae.movimentos.Count; j++)
                        {
                            filho2.movimentos.Add(mae.movimentos[j]);
                        }

                        //Mutação - muta 3 movimento de cada cromossomo filho gerado
                        int cromossomoMutar = 0;

                        cromossomoMutar = rand.Next(0, melhoresCromossomos.FirstOrDefault().movimentos.Count - 1);
                        filho1.movimentos[cromossomoMutar] = rand.Next(0, 9);

                        cromossomoMutar = rand.Next(0, melhoresCromossomos.FirstOrDefault().movimentos.Count - 1);
                        filho2.movimentos[cromossomoMutar] = rand.Next(0, 9);

                        //garantir que a proxima população o mesmo tamanho da atual
                        if (novaGeracao.Count < populacao)
                        {
                            novaGeracao.Add(filho1);
                        }

                        if (novaGeracao.Count < populacao)
                        {
                            novaGeracao.Add(filho2);
                        }
                    }

                    //substituo a geração antiga pela nova
                    cromossomos = novaGeracao;
                }

                foreach (var item in cromossomos)
                {
                    //Coloca o agente na posição inicial
                    Agente agente = new Agente()
                    {
                        posicao = new Posicao()
                        {
                            coluna = 0, linha = 0
                        }
                    };

                    foreach (var movimento in item.movimentos)
                    {
                        if (movimento == 1)                                                                   //direita
                        {
                            if (!encontrouImpedimento(item, agente.posicao.linha, agente.posicao.coluna + 1)) //tem parede? ou to no boundary?
                            {
                                agente.posicao.coluna = agente.posicao.coluna + 1;
                                item.movimentosValidos.Add(new Posicao()
                                {
                                    coluna = agente.posicao.coluna, linha = agente.posicao.linha
                                });

                                if (encontrouSaida(agente.posicao.linha, agente.posicao.coluna))
                                {
                                    agenteEncontrouSaida = true;
                                    item.encontrouSaida  = true;
                                    break;
                                }
                            }
                        }
                        else if (movimento == 2)                                                              //esquerda
                        {
                            if (!encontrouImpedimento(item, agente.posicao.linha, agente.posicao.coluna - 1)) //tem parede? ou to no boundary?
                            {
                                agente.posicao.coluna = agente.posicao.coluna - 1;
                                item.movimentosValidos.Add(new Posicao()
                                {
                                    coluna = agente.posicao.coluna, linha = agente.posicao.linha
                                });

                                if (encontrouSaida(agente.posicao.linha, agente.posicao.coluna))
                                {
                                    agenteEncontrouSaida = true;
                                    item.encontrouSaida  = true;
                                    break;
                                }
                            }
                        }
                        else if (movimento == 3)                                                              //cima
                        {
                            if (!encontrouImpedimento(item, agente.posicao.linha - 1, agente.posicao.coluna)) //tem parede? ou to no boundary?
                            {
                                agente.posicao.linha = agente.posicao.linha - 1;
                                item.movimentosValidos.Add(new Posicao()
                                {
                                    coluna = agente.posicao.coluna, linha = agente.posicao.linha
                                });

                                if (encontrouSaida(agente.posicao.linha, agente.posicao.coluna))
                                {
                                    agenteEncontrouSaida = true;
                                    item.encontrouSaida  = true;
                                    break;
                                }
                            }
                        }
                        else if (movimento == 4)                                                              //baixo
                        {
                            if (!encontrouImpedimento(item, agente.posicao.linha + 1, agente.posicao.coluna)) //tem parede? ou to no boundary?
                            {
                                agente.posicao.linha = agente.posicao.linha + 1;
                                item.movimentosValidos.Add(new Posicao()
                                {
                                    coluna = agente.posicao.coluna, linha = agente.posicao.linha
                                });

                                if (encontrouSaida(agente.posicao.linha, agente.posicao.coluna))
                                {
                                    agenteEncontrouSaida = true;
                                    item.encontrouSaida  = true;
                                    break;
                                }
                            }
                        }
                        else if (movimento == 5)                                                                  //cima_direita
                        {
                            if (!encontrouImpedimento(item, agente.posicao.linha - 1, agente.posicao.coluna + 1)) //tem parede? ou to no boundary?
                            {
                                agente.posicao.linha  = agente.posicao.linha - 1;
                                agente.posicao.coluna = agente.posicao.coluna + 1;
                                item.movimentosValidos.Add(new Posicao()
                                {
                                    coluna = agente.posicao.coluna, linha = agente.posicao.linha
                                });

                                if (encontrouSaida(agente.posicao.linha, agente.posicao.coluna))
                                {
                                    agenteEncontrouSaida = true;
                                    item.encontrouSaida  = true;
                                    break;
                                }
                            }
                        }
                        else if (movimento == 6)                                                                  //cima_esquerda
                        {
                            if (!encontrouImpedimento(item, agente.posicao.linha - 1, agente.posicao.coluna - 1)) //tem parede? ou to no boundary?
                            {
                                agente.posicao.linha  = agente.posicao.linha - 1;
                                agente.posicao.coluna = agente.posicao.coluna - 1;
                                item.movimentosValidos.Add(new Posicao()
                                {
                                    coluna = agente.posicao.coluna, linha = agente.posicao.linha
                                });

                                if (encontrouSaida(agente.posicao.linha, agente.posicao.coluna))
                                {
                                    agenteEncontrouSaida = true;
                                    item.encontrouSaida  = true;
                                    break;
                                }
                            }
                        }
                        else if (movimento == 7)                                                                  //baixo_direita 7
                        {
                            if (!encontrouImpedimento(item, agente.posicao.linha + 1, agente.posicao.coluna + 1)) //tem parede? ou to no boundary?
                            {
                                agente.posicao.linha  = agente.posicao.linha + 1;
                                agente.posicao.coluna = agente.posicao.coluna + 1;
                                item.movimentosValidos.Add(new Posicao()
                                {
                                    coluna = agente.posicao.coluna, linha = agente.posicao.linha
                                });

                                if (encontrouSaida(agente.posicao.linha, agente.posicao.coluna))
                                {
                                    agenteEncontrouSaida = true;
                                    item.encontrouSaida  = true;
                                    break;
                                }
                            }
                        }
                        else if (movimento == 8)                                                                  //baixo_esquerda 8
                        {
                            if (!encontrouImpedimento(item, agente.posicao.linha + 1, agente.posicao.coluna - 1)) //tem parede? ou to no boundary?
                            {
                                agente.posicao.linha  = agente.posicao.linha + 1;
                                agente.posicao.coluna = agente.posicao.coluna - 1;
                                item.movimentosValidos.Add(new Posicao()
                                {
                                    coluna = agente.posicao.coluna, linha = agente.posicao.linha
                                });

                                if (encontrouSaida(agente.posicao.linha, agente.posicao.coluna))
                                {
                                    agenteEncontrouSaida = true;
                                    item.encontrouSaida  = true;
                                    break;
                                }
                            }
                        }
                    }

                    item.peso = calculaAptidao(item, caminhosLivres);

                    if (agenteEncontrouSaida)
                    {
                        break;
                    }
                }

                printaGeracao(count, cromossomos);

                var melhor = (from cromossomo in cromossomos
                              orderby cromossomo.encontrouSaida descending, cromossomo.peso descending
                              select cromossomo).FirstOrDefault();

                printaMelhorCromossomo(count, melhor);

                Console.WriteLine();
                Console.WriteLine();
                //Thread.Sleep(1000);
                count++;
            }

            // Keep the console window open in debug mode.
            Console.WriteLine("Press any key to exit.");
            Console.ReadKey();
        }