/// <summary> /// Avalia a Dama. /// </summary> /// <remarks> /// Mobilidade e ataques a casas perto ao rei pode ser addicionada. /// </remarks> /// <param name="pontuacao">Pontuação do lado branco ou preto</param> /// <param name="indice">Índice da casa da dama</param> private void AvaliaDama(Pontuacao pontuacao, int indice) { Fase -= Pontuacao.Fase.DAMA; pontuacao.Inicial += Pontuacao.Material.DAMA_INICIO; pontuacao.Final += Pontuacao.Material.DAMA_FINAL; pontuacao.Final += Pontuacao.Tabela.CENTRALIZACAO[Defs.Converte12x12Para8x8(indice)] / 2; }
/// <summary> /// Avalia o bispo. /// </summary> /// <remarks> /// Vai tentar centralizar o bispo. /// Outro item que pode ser adicionado é uma relação com os próprios peões, /// não estando no quadrado da mesma cor do bispo. /// </remarks> /// <param name="pontuacao">Pontuação do lado branco ou preto</param> /// <param name="indice">Índice da casa do bispo</param> private void AvaliaBispo(Pontuacao pontuacao, int indice) { Fase -= Pontuacao.Fase.BISPO; pontuacao.Inicial += Pontuacao.Material.BISPO_INICIO; pontuacao.Inicial += Pontuacao.Tabela.CENTRALIZACAO[Defs.Converte12x12Para8x8(indice)]; pontuacao.Final += Pontuacao.Material.BISPO_FINAL; pontuacao.Final += Pontuacao.Tabela.CENTRALIZACAO[Defs.Converte12x12Para8x8(indice)]; }
/// <summary> /// Avalia o Rei. /// </summary> /// <remarks> /// Apenas dá um bônus para peões na frente do rei, ou seja, escudo de peão (pawn shield). /// O que pode ser adicionado é uma penalidade para muitos peões inimigos perto do rei, o que /// significa que um ataque de peão está se formando. /// Especialmente em termos de segurança do rei, se as peças inimigas estão atacando a área próxima ao rei, /// pode ser um grande valor a este item. Em geral a segurança do rei é difícil de calibrar corretamente e /// requer muitos testes. /// Além disso, algo a considerar é a presença da dama inimiga ao avaliar a segurança do rei. Sem a dama o /// valor da segurança do rei reduz drasticamente. /// </remarks> /// <param name="pontuacao">Pontuação do lado branco ou preto</param> /// <param name="indice">Índice da casa do rei</param> /// <param name="dados_rei">Informação relativa à cor a ser avaliada.</param> private void AvaliaRei(Pontuacao pontuacao, int indice, DadosRei dados_rei) { for (int i = 0; i < dados_rei.DirecaoEmFrente.Length; i++) { if (Tabuleiro.ObtemPeca(indice + i) == dados_rei.PeaoAmigo) { pontuacao.Inicial += Pontuacao.Rei.PEAO_ESCUDO; } } pontuacao.Inicial += dados_rei.TabelaInicio[Defs.Converte12x12Para8x8(indice)]; }
/// <summary> /// Gera uma string FEN representando a posição atual no tabuleiro . /// </summary> /// <param name="tabuleiro">tabuleiro com a posição para gerar a string FEN</param> /// <returns>string FEN</returns> public static string ConverteTabuleiroParaFEN(Tabuleiro tabuleiro) { string fen = ""; fen += FEN.ObtemDescPosicao(tabuleiro); fen += " " + tabuleiro.CorJogar.ParaTexto(); fen += " " + FEN.ObtemDescRoque(tabuleiro); fen += " " + Defs.ObtemDescCasa(tabuleiro.IndiceEnPassant); fen += " " + tabuleiro.ContadorRegra50.ToString(); fen += " " + tabuleiro.ContadorMovimentos.ToString(); return(fen); }
/// <summary> /// Avalia peões pretos. /// </summary> /// <see cref="AvaliaPeaoBranco(int)"/> /// <param name="indice">Índice da casa do peão</param> private void AvaliaPeaoPreto(int indice) { Fase -= Pontuacao.Fase.PEAO; Preto.Inicial += Pontuacao.Material.PEAO_INICIO; Preto.Inicial += Pontuacao.Tabela.PEAO_PRETO[Defs.Converte12x12Para8x8(indice)]; if (indice == (int)Defs.INDICE.D5 || indice == (int)Defs.INDICE.E5) { Preto.Inicial += Pontuacao.Peao.PEAO_CENTRAL_1; } if (indice == (int)Defs.INDICE.D6 || indice == (int)Defs.INDICE.E6) { Preto.Inicial += Pontuacao.Peao.PEAO_CENTRAL_2; } Preto.Final += Pontuacao.Material.PEAO_FINAL; Preto.Final += Pontuacao.Tabela.PEAO_PRETO[Defs.Converte12x12Para8x8(indice)]; }
/// <summary> /// Avalia peões brancos. /// </summary> /// <remarks> /// Valor material, valor na casa de acordo com uma tabela e bônus de centralização. /// Outros termos que podems ser adicionados: /// Peões passados: bônus para peões que não podem ser capturados por peões inimigos. /// Peões isolados: penalidade para peões sem peões amigos em colunas vizinhas. /// Peões conectados: bônus para peões que se apoiam. /// Peões duplicados: penalidade para peões na mesma coluna. /// Peões para trás: penalidade para peões que não podem avançar sem encontrar os peões inimigos. /// Peões candidatos: bônus para peões que podem se tornar passados. /// Outra idéia é armazenar o valor da estrutura de peões em uma tabela, e quando encontramos /// a mesma estrutura de peões mais tarde, podemos usar o valor calculado dessa tabela. Isso ajuda /// o desempenho, pois a estrutura do peão pode repetir muito em uma pesquisa. /// Neste caso, podemos usar uma chave zobrist para a estrutura do peão. Normalmente, isso é /// chamado de "pawn hash table". /// </remarks> /// <param name="indice">Índice da casa do peão</param> private void AvaliaPeaoBranco(int indice) { Fase -= Pontuacao.Fase.PEAO; Branco.Inicial += Pontuacao.Material.PEAO_INICIO; Branco.Inicial += Pontuacao.Tabela.PEAO_BRANCO[Defs.Converte12x12Para8x8(indice)]; if (indice == (int)Defs.INDICE.D4 || indice == (int)Defs.INDICE.E4) { Branco.Inicial += Pontuacao.Peao.PEAO_CENTRAL_1; } if (indice == (int)Defs.INDICE.D3 || indice == (int)Defs.INDICE.E3) { Branco.Inicial += Pontuacao.Peao.PEAO_CENTRAL_2; } Branco.Final += Pontuacao.Material.PEAO_FINAL; Branco.Final += Pontuacao.Tabela.PEAO_BRANCO[Defs.Converte12x12Para8x8(indice)]; }
/// <summary> /// Retorna a lista ordenada de movimentos. /// </summary> /// <remarks> /// Ordem de classificação: /// 1. Movimento melhor vindo da tabela de transposição. /// 2. Capturas /// 3. Movimentos simples classificados pela tabela de valores. /// /// Falta aqui os movimentos matadores (killer moves). Quase todos os programas de xadrez /// usam esta técnica. Eu me pergunto se alguém poderia fazer isso e se faria o Enxadrista /// um pouco melhor. /// </remarks> /// <param name="cor">Cor do lado com movimentos a ordenar.</param> /// <param name="lista">List de movimentos.</param> /// <param name="melhor">Melhor movimento que será ordenado primeiro, normalmente da tabela de transposição</param> /// <returns>Lista ordenada de movimentos.</returns> public List <Movimento> Orderna(Cor cor, List <Movimento> lista, Movimento melhor) { foreach (var movimento in lista) { if (movimento.Equals(melhor)) { movimento.ValorOrdenacao = 100000000; continue; } if (movimento.Captura()) { movimento.ValorOrdenacao = ValorCaptura(movimento) * 10000; continue; } int indice_peca = IndicePeca(movimento.Peca); int indice_casa = Defs.Converte12x12Para8x8(movimento.IndiceDestino); movimento.ValorOrdenacao = Tabela[indice_peca][indice_casa]; } return(lista.OrderByDescending(m => m.ValorOrdenacao).ToList()); }
/// <summary> /// Atualiza informação sempre que um bom movimento é encontrada. /// </summary> /// <remarks> /// Note que consideramos somente os movimentos simples. Captura já possui /// um valor maior do que movimentos simples. /// </remarks> /// <param name="cor">Cor do lado fazendo o movimento.</param> /// <param name="movimento">Movimento considerado bom.</param> /// <param name="profundidade">Profundidade que o movimento foi pesquisado.</param> public void AtualizaHistoria(Cor cor, Movimento movimento, int profundidade) { if (movimento.Tatico()) { return; } int indice_peca = IndicePeca(movimento.Peca); int indice_casa = Defs.Converte12x12Para8x8(movimento.IndiceDestino); Tabela[indice_peca][indice_casa] += profundidade; // Se o valor na tabela estiver muito alto, é feito um ajuste a todos valores. if (Tabela[indice_peca][indice_casa] > 9000) { for (int peca = 0; peca < NUMERO_PECAS; peca++) { for (int casa = 0; casa < NUMERO_CASAS; casa++) { Tabela[peca][casa] /= 8; } } } }
/// <summary> /// Converte uma string FEN para o tabuleiro. /// </summary> /// <param name="fen">string FEN representando uma posicão</param> /// <param name="tabuleiro">tabuleiro a ser atualizado com a posicão FEN</param> public static void ConverteFenParaTabuleiro(string fen, Tabuleiro tabuleiro) { string[] partes = fen.Split(' '); string posicao_pecas = partes[0]; string cor_jogar = partes[1]; string estado_roque = partes[2]; string casa_en_passant = partes[3]; string contador_regra_50 = partes.Length > 4 ? partes[4] : "0"; string contador_movimentos = partes.Length > 4 ? partes[5] : "0"; int fileira = Defs.PRIMEIRA_FILEIRA; int coluna = Defs.PRIMEIRA_COLUNA; foreach (var caracter in posicao_pecas) { if (char.IsDigit(caracter)) { coluna += int.Parse(caracter.ToString()); } else { switch (caracter) { case '/': fileira += 1; coluna = Defs.PRIMEIRA_COLUNA; break; default: tabuleiro.ColocaPeca(fileira * Defs.NUMERO_COLUNAS + coluna++, caracter.ParaPeca()); break; } } } tabuleiro.CorJogar = cor_jogar.ParaCor(); if (estado_roque.Contains('K')) { tabuleiro.RoqueE1G1 = true; } if (estado_roque.Contains('Q')) { tabuleiro.RoqueE1C1 = true; } if (estado_roque.Contains('k')) { tabuleiro.RoqueE8G8 = true; } if (estado_roque.Contains('q')) { tabuleiro.RoqueE8C8 = true; } tabuleiro.IndiceEnPassant = Defs.ObtemIndiceDaPosicao(casa_en_passant); tabuleiro.ContadorRegra50 = int.Parse(contador_regra_50); tabuleiro.ContadorMovimentos = int.Parse(contador_movimentos); }