Identificando e coletando dados importantes
Explorei diversos algoritmos para otimizar e reduzir o espaço de procura para todos os combos possíveis. No entanto, o vestimenta de cada epístola poder romper duas vezes aumentou o número de combinações potenciais, tornando difícil rastrear e validar cada uma delas. Enquanto competia no Codeforces, encontrei um problema que me lembrou do ‘problema da ilhéu,’ o que me deu uma novidade visão sobre porquê abordar o sistema avaliador de mãos.
Podemos simbolizar a mão porquê uma grade 2D de tamanho 4×13, onde cada poste representa as classificações de 1 a 13 e cada risco corresponde aos 4 naipes. Cada célula nesta grade contém a enumeração de cartas na mão, no nosso caso 1, 2 ou 0. Isso nos permite dividir a mão em “ilhas”, que são definidas porquê grupos de células terrestres conectadas com contagens de 1 ou 2 com base nas seguintes regras de conectividade:
1. Duas células são consideradas conectadas se compartilharem um lado (esquerdo, recta, supra ou aquém) na grade.
2. Todas as células dentro da mesma poste também serão conectadas se ambas contiverem pelo menos 1s, mesmo que não sejam adjacentes (supra ou aquém).
EXP da ‘mão A’: 11C 3H 4H 11D 3D 5H 9D 2H 6H 3C 4H 3D 4D 5H 12D 3C
A nossa primeira tarefa é identificar e rotular todas as ilhas distintas. Uma vez que cada ilhéu é independente das outras, podemos facilitar nossa vida mapeando cada ilhéu para um tipo de classe, vamos chamá-lo de _cardGraph. Esta classe será responsável por aquela ilhéu em termos de operações de extração, modificação ou exclusão.
Para maior perspicuidade, vamos isolar uma ilhéu e trabalhar nela nas próximas seções, para que seja mais fácil de seguir. Se ajudar, você pode pensar em cada ilhéu porquê um gráfico conectado, conforme mostrado na figura aquém:
Agora, se você pegar vários exemplos de ilhas e tentar extrair os combos possíveis, notará que algumas cartas têm funções únicas na ramificação para combinações potenciais. Chamaremos esse tipo Sua visita nos ajuda a continuar oferecendo o melhor para você! cartão de pontos de controle ou Cpts em suma, pois desempenham um papel necessário ao reduzir significativamente o espaço de pesquisa, porquê você verá nas etapas a seguir.
Cpts: Para que uma epístola seja considerada Cpts, ela deve estar em uma posição onde temos que escolher em qual combinação (run ou set) anexá-la. Se uma epístola puder caber naturalmente em múltiplas combinações sem forçar uma escolha (por exemplo, uma epístola duplicada com duas opções de combinações, cada epístola será anexada a uma combinação), ela não será considerada um Cpts.
No caso do nosso exemplo de ilhéu, o 3 de coração é identificado porquê cpts. Aquém estão todas as combinações às quais o 3 de Copas poderia se unir, uma de cada vez.
Nosso próximo passo é marcar cada cartão que se qualifica porquê Cpts. Para fazer isso, criaremos uma tábua 4×13 (no tipo byte), vamos chamá-la de _flagMap . Agora, para eficiência de memória, você pode tornar esta tábua compartilhada, cada instância _cardGraph criada manualmente pode referenciá-la e usá-la. Nesta tábua, a cada cartão em uma ilhéu será atribuído um fluxo de bits no índice correspondente em _flagMap, levante byte representará suas colocações potenciais em diferentes execuções ou conjuntos. Se um cartão for qualificado porquê Cpts, ele será armazenado em uma rima (precisaremos mais tarde), que chamaremos de _cptsStack. Cá está um detalhamento da estrutura de bytes: o primeiro bit indica se o cartão pertence a uma realização, o segundo bit indica sua colocação em uma realização suplementar, o terceiro bit representa se pertence a um conjunto e o quarto bit especifica se pertence. para vários conjuntos.
Cá está um exemplo de bitstream: 00000111 Cá temos:
• O primeiro bit (1) significa que o cartão pode pertencer a uma corrida.
• O segundo bit (1) significa que o cartão pode pertencer a uma segunda realização.
• O terceiro bit (1) significa que o cartão pertence a um conjunto.
Sua visita nos ajuda a continuar oferecendo o melhor para você! pf pv ph pi pj pw pl pm pn px pp pq pr gn bk">• O quarto bit (0) significa que o cartão não pertence a um segundo conjunto.
Poderíamos estar no caso em que a forma é 00000101 para um cartão (sem imitação), significando que o cartão pertence a uma corrida ou conjunto. Ou outra forma pode ser 00000011, o que significa que a placa pertence a duas execuções diferentes.
Para identificar um cpts, basta narrar os ‘1’s em sua representação de bits. Se esta enumeração ultrapassar o número totalidade daquela epístola na mão, é considerado um cpts. Por exemplo, se um cartão aparece duas vezes (ou seja, tem duas cópias) e sua representação de bits é 00000101, não é um cpts. No entanto, se a representação do bit for 00000111 porquê no exemplo, logo ela se qualificará porquê cpts.
Em nosso exemplo de ilhéu, esta seria a fisionomia da tábua _flagMap:
Depois de preenchermos o _flagMap e identificarmos os cpts, a próxima tarefa é desintegrar a ilhéu em linhas horizontais e verticais. Mas por que? Dividir o gráfico do cartão nessas linhas simplifica o processo de identificação de execuções e conjuntos, pois nos permite focar em sequências contíguas de cartões que podem ser processadas com mais eficiência. Uma vez que você pode imaginar, as linhas verticais representarão os conjuntos, enquanto as linhas horizontais representarão as execuções.
Armazenaremos cada risco nivelado em uma lista do tipo tupla, onde o primeiro item representa o índice inicial da risco e o último item representa o índice final (inclusive). Para as linhas verticais, basta simplesmente armazenar o índice da poste em uma lista.
Dica: Podemos realizar esta tarefa junto com a lanço de representação de bits em um único loop, alcançando dificuldade O(n).
Gerar Combos
Agora vamos fazer uma pausa e recapitular: identificamos os pontos de controle (CPTs) e os armazenamos no _cptsStack. Também decompomos a ilhéu em linhas verticais e horizontais e preenchemos o _flagMap com representação de bits de cartão.
Com nossos dados em vigor, o que resta é utilizá-los para gerar todos os combos válidos possíveis da ilhéu. Mas porquê fazemos isso? Cá está uma abordagem simplificada:
1. Atribuir posicionamentos válidos para os pontos de controle (Cpts):
Pegamos a representação de bits de um cpts de _flagMap, que indica todos os posicionamentos possíveis para esse cpts. Em seguida, observamos o número de cópias dos cpts no _cardGraph e ajustamos sua representação de bits para uma forma válida atual. Por exemplo, se o cpts tiver uma representação de bits de 00001111 e 2 cópias, podemos gerar todos os posicionamentos válidos para ele, que é C(4,2)=6C(4,2) = 6C(4,2)=6. As combinações possíveis seriam 0011, 0101, 1100, 1010, 1001 e 0110.
2. Usando DFS para configurar todas as combinações possíveis para cada Cpts:
Usaremos uma pesquisa em profundidade (DFS) para iterar sobre os posicionamentos válidos para cada cpts, conforme mostrado na lanço 1. Cada nó na árvore DFS representa um posicionamento verosímil para um determinado cpts, portanto, cada caminho DFS restrito representa um válido forma combinada. Para cada nó “folha” (final do caminho DFS), passamos para a próxima lanço.
3. Gerando Combos:
Nesta lanço, iteramos sobre as linhas horizontais e verticais da ilhéu para identificar execuções, conjuntos e uma lista de resíduo. Isso é feito em duas passagens para cada risco, porquê segue:
- Passe 1: Para uma risco nivelado, por exemplo, acrescentamos continuamente cartões de [line start to line end] em uma lista para formar uma corrida. Paramos de somar if ( card_bit_representation | 00000001 == 0 ). Se a duração da corrida for maior ou igual a 3, adicionamos ao combo de corrida; caso contrário, cada cartão vai para a lista de resíduo e continuamos tentando formar outra realização até chegar ao final da risco.
- Passe 2: Repita o processo, desta vez procurando cartões que correspondam a um padrão de bits dissemelhante com a operação ou (00000010). Isso nos permite identificar possíveis segundas execuções.
A mesma abordagem se aplica à extração de conjuntos, mas usamos operações de bits com 00000100 e 00001000.
4. Registre o Combo Válido e Passe para a Próxima Feitio DFS:
Depois de concluir todas as execuções, conjuntos e dumps do combo atual, salvamos o combo e passamos para a próxima forma do DFS para repetir o processo. Desta forma, exploramos sistematicamente todas as configurações potenciais para combos válidos.
se você codificou tudo corretamente e alimentou nosso exemplo de ilhéu: ”2H3H4H5H4H5H6H3C3C3D3D4D”, ele deve ser podre conforme mostrado aquém. Observe que adicionei alguns cálculos a cada combo gerado para que possamos ter uma noção de porquê a IA irá agir.
No próximo cláusula, vou me aprofundar no resto do sistema, focando na modificação dinâmica da mão e na estratégia de IA. Se você acompanhou até cá, não será difícil perceber porquê podemos otimizar a soma e remoção de cartas, além de incorporar as duas regras que deixamos de lado no início. Fique ligado e até a próxima! “espero que 😉”.
Salvo indicação em contrário, todas as imagens são criadas pelo responsável usando Lucidchart, Gimp e Python
Tags:
Crédito: Natividade Original