TProcura
Biblioteca em C++ para testes paramétricos de algoritmos, e coleção de algoritmos de procura e otimização
Loading...
Searching...
No Matches
TProcuraAdversa.cpp
Go to the documentation of this file.
1#include "TProcuraAdversa.h"
2#include <stdio.h>
3#include <time.h>
4#include <math.h>
5#include <string.h>
6#ifdef MPI_ATIVO
7#include <mpi.h>
8#endif
9
10// valor de infinito (vitoria/derrota), omissao 1000
12// controlo para indicar se a procura foi realizada de forma completa (c.c. foi cortada)
14// hashtable / valor e nível obtido
16// profundidade máxima no método iterativo
18// número de vezes que uma avaliação é reutilizada
22
23TProcuraAdversa::TProcuraAdversa(void) : minimizar(true), indiceHT(-1)
24{
25
26}
27
31
33{
35
36 // adicionar parâmetros da procura adversa
37 // alterar algoritmos
38 parametro[ALGORITMO] = { "ALGORITMO",2,1,2,"Seleção do algoritmo de procura adversa base.",
39 { "MiniMax", "MiniMax alfa/beta" } };
40
41 Parametro(LIMITE) = 0; // procura iterativa preferencial
42 Parametro(ESTADOS_REPETIDOS) = IGNORADOS; // nas procuras adversas, não utilizar este parametro (utilizar ordenar=2)
43 Parametro(BARALHAR_SUCESSORES) = 0; // por omissão está com valor 0, para facilitar nos testes, mas deve ficar com 1 para obter jogos distintos
44
45 // O "infinito" é dependente do problema, não faz sentido alterar senão no código
46
47 parametro[RUIDO_HEURISTICA].dependencia = {}; // faz sentido com qualquer algoritmo de procura adversa, ao contrário das procuras construtivas
48
49 // adicionar parametros da procura adversa
50 parametro += {
51 { "ORDENAR_SUCESSORES", 2, 0, 2, "0 não ordena sucessores, 1 ordena por heurística, 2 usa o melhor valor de procuras anteriores." },
52 { "PODA_HEURISTICA",0,0,1000, "0 não existe poda, caso contrário é o número máximo de sucessores a considerar (tem de se ordenar sucessores)." },
53 { "PODA_CEGA",0,0,10000, "Igual a PodaHeuristica, mas é efetuado de forma aleatória, sem calcular a heurística. Utilizar um valor sempre maior que Poda. " },
54 { "HEUR_BASE", 200, 100, 1000, "Valor base para diferença entre ameaças de K e K-1 (100 não há diferença, 200 corresponde ao dobro e é o valor por omissão)" }
55 };
56}
57
58
64
65void TProcuraAdversa::DebugChamada(bool noFolha, int alfa, int beta) {
66 bool raiz = (ramo.Count() <= 1);
67
68 if (Parametro(NIVEL_DEBUG) == ATIVIDADE && expansoes % 1000 == 0)
69 printf("#");
71 if (raiz)
73 NovaLinha(true);
76 DebugEstado(false);
77 if (alfa || beta)
78 printf(" α=%d β=%d ═══", alfa, beta);
79 if (pai != NULL)
80 printf(" %-2s%s", Icon(EIcon::ACCAO), *pai->Acao(this)); // mostra sempre a ação
81 if ((noFolha && Parametro(NIVEL_DEBUG) >= DETALHE) ||
83 Debug();
84 }
85}
86
87
89// Algoritmo MiniMax
91// retorna o valor do estado actual, apos procura de profundidade nivel
93{
94 bool noFolha = (nivel == 1 || Parar());
95
96 if (nivel == 0)
97 return MetodoIterativo(false);
98
99 DebugChamada(noFolha);
100 // no final da árvore, retornar (valor da heuristica)
101 if (noFolha) {
102 completo = false; // árvore cortada, a procura deixa de ser completa
103 return NoFolha(true);
104 }
105
106 // expandir o estado
107 TVector<TNo> sucessores;
108 Sucessores(sucessores);
109 // caso não existam sucessores, é como se fosse um nó folha
110 if (sucessores.Empty())
111 return NoFolha(false);
112
113 TVector<int> id; // índice para ordenar os sucessores por heurística
114 OrdenarSucessores(sucessores, id, nivel);
115
116 int resultado = 0, melhor = -1;
117 // processar os sucessores
118 for (int i = 0; i < id.Count(); i++) {
119 DebugExpansao(i, id.Count(), minimizar);
120 int valor;
121 TValorEstado valorConhecido;
122
123 if (((TProcuraAdversa*)sucessores[id[i]])->ValorEstado(valorConhecido, LER) &&
124 valorConhecido.nivel >= nivel)
125 {
126 valor = valorConhecido.valor;
127 if (Parametro(NIVEL_DEBUG) >= PASSOS) {
128 ((TProcuraAdversa*)sucessores[id[i]])->DebugChamada(false);
129 DebugFolha(false, "%-2s%d", Icon(EIcon::MEMORIA), valor);
130 }
131 }
132 else {
133 // chamada recursiva, com um nível a menos, idêntico à procura em profundidade
134 valor = ((TProcuraAdversa*)sucessores[id[i]])->MiniMax(nivel - 1);
135 valorConhecido = { valor, nivel, EXATO };
136 ((TProcuraAdversa*)sucessores[id[i]])->ValorEstado(valorConhecido, GRAVAR);
137 }
138
139 // pretende-se obter o melhor resultado, que oscila entre minimizar ou maximizar
140 if (i == 0 || (minimizar ? resultado > valor : resultado < valor)) {
141 resultado = valor;
142 melhor = id[i];
143 if (nivel > 1 && Parametro(NIVEL_DEBUG) >= PASSOS) { // colocar valor actual alterado
145 NovaLinha();
146 printf(" %d", resultado);
147 }
148 // caso de vitória/derrota
150 DebugFolha(true, " %-2s%d", Icon(resultado < 0 ? EIcon::VIT_PRETA : EIcon::VIT_BRANCA), custo);
151 // listar os nós não explorados
152 if (Parametro(NIVEL_DEBUG) >= PASSOS) {
154 for (int j = i + 1; j < id.Count(); j++)
155 valores += sucessores[id[j]]->debugID;
156 MostraConjunto(valores, Icon(EIcon::ID));
157 }
158 break; // nao e possivel melhorar
159 }
160 }
161 }
162 // todos os sucessores analizados, se houver uma solução melhor, retornar
163 if (melhor >= 0) {
164 if (solucao != NULL)
165 delete solucao;
167 }
170 return resultado;
171}
172
175 heuristica = TProcuraConstrutiva::Heuristica(); // incrementa avaliações e adiciona ruído se for caso disso
176 // valor da heurística já calculado, gravar
177 calculado = { heuristica,0,EXATO }; // valor da heurística com nível 0
179 return heuristica;
180}
181
182// chamar em CSubProblema::Heuristica() para verificar se a heurística já existe, ou precisa de ser calculada
185 if (ValorEstado(calculado, LER)) {
187 return true;
188 }
189 return false;
190}
191
193 TVector<TNo>& sucessores, TVector<int>& id, int nivel)
194{
195 if (Parametro(PODA_CEGA) > 0 &&
196 sucessores.Count() > Parametro(PODA_CEGA))
197 {
198 // podar de forma aleatória, nem heurística deve ser calculada
199 while (sucessores.Count() > Parametro(PODA_CEGA)) {
200 int id = TRand::rand() % sucessores.Count();
201 delete sucessores[id];
202 sucessores[id] = sucessores.Last();
203 sucessores.Pop();
204 }
205 }
206
207 id = {};
208 if (nivel > 2 && Parametro(ORDENAR_SUCESSORES) >= 1) {
209 CalcularHeuristicas(sucessores, &id); // id fica ordenado por heurística
210
211 if (!minimizar)
212 id.Invert();
213
214 // podar sucessores
215 if (Parametro(PODA_HEURISTICA) > 0 &&
216 Parametro(PODA_HEURISTICA) < id.Count()) {
218 for (int i = 0; i < Parametro(PODA_HEURISTICA); i++)
219 manterSuc += sucessores[id[i]];
220 for (int i = Parametro(PODA_HEURISTICA); i < id.Count(); i++)
221 delete sucessores[id[i]];
223 id = {};
224 for (int i = 0; i < sucessores.Count(); i++)
225 id += i;
226 }
227
228 }
229 else
230 for (int i = 0; i < sucessores.Count(); i++)
231 id += i;
232}
233
234// iteração, aumentando o nível progressivamente
236 int resultado = 0, resOK = 0, nivel = 0;
237 nivelOK = 0;
238 TNo solOK = NULL;
239 do {
240 DebugIteracao(nivel + 1, Icon(EIcon::LIMITE));
241 completo = true;
242 // chamar a profundidade nível 1, e se não resolver, o nível 2, e assim sucessivamente
243 resultado = (alfaBeta ? MiniMaxAlfaBeta(++nivel) : MiniMax(++nivel));
244 if (!Parar() || nivel == 0) {
245 // guardar a última solução realizada sem interrupções, bem como o resultado
246 if (solOK != NULL)
247 delete solOK;
248 solOK = solucao;
249 solucao = NULL;
251 nivelOK = nivel;
252 if (Parametro(NIVEL_DEBUG) > NADA && solOK != NULL)
253 printf("\n │ %-2s%-2s %d %-2s%s %-2s%d ",
254 Icon(EIcon::ARVORE), Icon(EIcon::LIMITE), nivel,
255 Icon(EIcon::ACCAO), *Acao(solOK), Icon(EIcon::SUCESSO), resultado);
256 }
257 else
258 completo = false;
259 } while (!completo && !Parar());
260 if (solucao != NULL)
261 delete solucao;
262 solucao = solOK;
263 return resOK;
264}
265
266// Utilitário para calculo de uma heurística standard em jogos simples (tipicamente sem empates):
267// - calcular o número de ameaças de vitória, para cada lado, de menor comprimento:
268// - qMin - vetor com número de ameaças (1 ou mais) a 1 jogada (na primeira posição), a 2 (na segunda posição), e assim sucessivamente;
269// - qMax - vetor com número de ameaças (1 ou mais) a 1 jogada (na primeira posição), a 2 (na segunda posição), e assim sucessivamente;
270int TProcuraAdversa::MaiorAmeaca(TVector<int>& qMin, TVector<int>& qMax, int maxAmeaca) const
271{
272 double pontos = 0;
273 double base = Parametro(HEUR_BASE) / 100.0;
274 double peso = 1;
275
276 // verificar situações de ganho imediato
277 if (!minimizar && qMax.First() > 0) {
278 return infinito; // Vitória imediata para o max
279 }
280 if (minimizar && qMin.First() > 0) {
281 return -infinito; // Vitória imediata para o min
282 }
283
284 // Ameaças iguais a maxAmeaca ou superior, valem 1, todas as outras valem conforme
285 peso = 1;
286 for (int i = qMin.Count() - 1; i >= 0; i--) {
287 pontos -= qMin[i] * peso;
288 if (i < maxAmeaca) // peso começa a aumentar
289 peso *= base;
290 }
291 peso = 1;
292 for (int i = qMax.Count() - 1; i >= 0; i--) {
293 pontos += qMax[i] * peso;
294 if (i < maxAmeaca) // peso começa a aumentar
295 peso *= base;
296 }
297
298 return (int)(0.5 + infinito * (2.0 / (1.0 + exp(-pontos * 2.0 / infinito)) - 1.0));
299}
300
301// fim da procura, por corte de nível (ou não haver sucessores), retornar heurística
303 int resultado = Heuristica();
304
305 if (resultado >= infinito) {
306 resultado = infinito - custo; // subtrair
307 // jogo ganho pelo branco
308 // se o agente minimiza, tentar o jogo mais longo possível
309 // caso contrário, quanto maior o jogo pior, quer o jogo mais curto
310 // a minimizar, entre um nível 10 e 20, irá preferir o 20, já que -20 é menor que -10
311 // a maximizar, entre um nível 10 e 20, irá preferir o 10, já que 10 é menor que 20
312 }
313 else if (resultado <= -infinito) {
314 resultado = custo - infinito; // somar
315 // jogo ganho pelo preto
316 // se o agente maximiza, tentar o jogo mais longo possível
317 // caso contrário, quanto maior o jogo pior, quer o jogo mais curto
318 // a maximizar, entre 10 e 20, irá preferir 20, sempre é maior
319 // a minimizar, entre 10 e 20, irá preferir 10 que é menor
320 }
321 DebugFolha(false, "%-2s%d", Icon(EIcon::FOLHA), resultado);
322 return resultado;
323}
324
326// Algoritmo MiniMaxAlfaBeta
328// retorna o valor do estado actual, apos procura de profundidade nivel
329// idêntico a MiniMax
330int TProcuraAdversa::MiniMaxAlfaBeta(int nivel, int alfa, int beta)
331{
332 bool noFolha = (nivel == 1 || Parar());
333
334 if (nivel == 0)
335 return MetodoIterativo(true);
336
338 if (noFolha) {
339 completo = false;
340 return NoFolha(true);
341 }
342
345 if (sucessores.Empty())
346 return NoFolha(false);
347
348 TVector<int> id; // índice para ordenar os sucessores por heurística
349 OrdenarSucessores(sucessores, id, nivel);
350
351 int resultado = 0, melhor = -1;
352 for (int i = 0; i < id.Count(); i++) {
353 DebugExpansao(i, id.Count(), minimizar);
354 int valor;
356
359 {
360 valor = valorConhecido.valor;
361 if (Parametro(NIVEL_DEBUG) >= PASSOS) {
362 ((TProcuraAdversa*)sucessores[id[i]])->DebugChamada(false, alfa, beta);
363 DebugFolha(false, "%-2s%d", Icon(EIcon::MEMORIA), valor);
364 }
365 }
366 else {
367 // chamada recursiva, com um nível a menos, idêntico à procura em profundidade
368 valor = ((TProcuraAdversa*)sucessores[id[i]])->MiniMaxAlfaBeta(nivel - 1, alfa, beta);
369 valorConhecido = { valor, nivel, EXATO };
370 if (valor <= alfa)
371 valorConhecido.tipo = UPPER_BOUND; // Causado por corte alfa
372 else if (valor >= beta)
373 valorConhecido.tipo = LOWER_BOUND; // Causado por corte beta
374 // registar valor apenas se estiver dentro de alfa/beta (para não influenciarem o resultado)
375 ((TProcuraAdversa*)sucessores[id[i]])->ValorEstado(valorConhecido, GRAVAR);
376 }
377
378 if (i == 0 || (minimizar ? resultado > valor : resultado < valor)) {
379 resultado = valor;
380 melhor = id[i];
381 if (nivel > 1 && Parametro(NIVEL_DEBUG) >= PASSOS) {
383 NovaLinha();
384 printf(" %d", resultado);
385 }
386 }
387 // corte alfa/beta bem como atualização
388 if (i < sucessores.Count() - 1) { // nao interessa cortar quando não há mais nada para cortar
390 // listar os nós não explorados
391 if (Parametro(NIVEL_DEBUG) >= PASSOS) {
393 for (int j = i + 1; j < id.Count(); j++)
394 valores += sucessores[id[j]]->debugID;
395 MostraConjunto(valores, Icon(EIcon::ID));
396 }
397 break;
398 }
399 }
400 }
401 if (melhor >= 0) {
402 if (solucao != NULL)
403 delete solucao;
405 }
408 return resultado;
409}
410
411bool TProcuraAdversa::Utilizavel(TValorEstado& valor, int nivel, int alfa, int beta) {
412 return valor.nivel >= nivel &&
413 (valor.tipo == EXATO ||
414 (valor.tipo == LOWER_BOUND && valor.valor >= beta) ||
415 (valor.tipo == UPPER_BOUND && valor.valor <= alfa));
416}
417
418
419// verifica se há um corte alfa/beta, atualizando alfa e beta
420bool TProcuraAdversa::CorteAlfaBeta(int valor, int& alfa, int& beta) {
421 if (minimizar) { // pretas
422 // ver se ja e maximo
423 if (valor <= custo + 1 - infinito) {
424 DebugFolha(true, "%-2s%d", Icon(EIcon::VIT_PRETA), custo);
425 return true;
426 }
427 if (alfa >= valor) {
428 // corte alfa
429 DebugFolha(true, "%-2sα(%d)", Icon(EIcon::CORTE), alfa);
430 return true; // as brancas tem uma alternativa, e escusado continuar a procurar aqui
431 }
432 // atualização beta
433 if (beta > valor) {
434 beta = valor;
435 Debug(PASSOS, false, " → β");
436 }
437 }
438 else { // brancas
439 // ver se atingiu o maximo
440 if (valor >= infinito - custo - 1) {
441 DebugFolha(true, "%-2s%d", Icon(EIcon::VIT_BRANCA), custo);
442 return true;
443 }
444 if (beta <= valor) {
445 // corte beta
446 DebugFolha(true, "%-2sβ(%d)", Icon(EIcon::CORTE), beta);
447 return true; // as pretas tem uma alternativa, e escusado continuar a procurar aqui
448 }
449 // atualização alfa
450 if (alfa < valor) {
451 alfa = valor;
452 Debug(PASSOS, false, " → α");
453 }
454 }
455 // não há corte
456 return false;
457}
458
459// utilizar para executar testes empíricos, utilizando todas as instâncias,
460// Utiliza as configurações existentes, ou parâmetros atuais
461// Efetua um torneio entre configurações
463 TVector<TResultadoJogo> resultados; // guarda resultado dos jogos (qualquer ordem)
464 TVector<int> atual;
465 double periodoReporte = 60;
467 int nTarefa = 0;
468
469 TesteInicio(instancias, atual);
470
471 switch (Parametro(NIVEL_DEBUG)) {
472 case DETALHE: periodoReporte = 10; break;
473 case COMPLETO: periodoReporte = 0; break; // reporte em todos os eventos
474 }
475 Cronometro(CONT_REPORTE, true); // reiniciar cronómetro evento
476
477 TVector<TVector<int>> torneio; // pares de configurações: 1 melhor, 0 igual -1 pior
479 for (int i = 0; i < configuracoes.Count(); i++) {
480 torneio[i].Count(configuracoes.Count());
481 torneio[i].Reset(0);
482 }
483
484 if (mpiID == 0)
485 Debug(ATIVIDADE, false,
486 "\n ├─ %-2sTarefas:%d %-2sInstâncias: %d %-2sConfigurações: %d %-2sProcessos: %d.",
487 Icon(EIcon::TAREFA), instancias.Count() * configuracoes.Count() * (configuracoes.Count() - 1),
488 Icon(EIcon::INST), instancias.Count(),
489 Icon(EIcon::CONF), configuracoes.Count(),
490 Icon(EIcon::PROCESSO), mpiCount) &&
491 fflush(stdout);
492
493 // dois jogadores, brancas é o primeiro a jogar, pretas é o segundo
494 for (int brancas = 0; brancas < configuracoes.Count(); brancas++)
495 for (int pretas = 0; pretas < configuracoes.Count(); pretas++)
496 if (brancas != pretas) {
497 for (auto inst : instancias) {
498 // distribuir tarefas por MPI
499 if ((nTarefa++) % mpiCount != mpiID)
500 continue;
501
502 resultados += {inst, brancas, pretas};
503
505 Debug(ATIVIDADE, false,
506 "\n ├─ %-2s%-15s %-2s%-5d %-2s%-5d %-2s%-5d %-2s%-5d %-2s%-5d",
507 Icon(EIcon::TEMPO), *MostraTempo(Cronometro(CONT_TESTE)),
508 Icon(EIcon::TAREFA), nTarefa,
509 Icon(EIcon::INST), inst,
510 Icon(EIcon::CONF), brancas + 1,
511 Icon(EIcon::CONF), pretas + 1,
512 Icon(EIcon::PROCESSO), mpiCount) &&
513 fflush(stdout);
515 }
516 ExecutaTarefa(resultados, inst, brancas, pretas, &torneio);
517 }
518 }
519
520 if (ficheiro.Empty()) {
521 MostrarTorneio(torneio, true);
523 }
524 else {
526 printf("\n │ Ficheiro %s gravado.", *ficheiro);
527 }
528
529 if (mpiCount > 1 && modoMPI == 0)
530 // tenta juntar ficheiros, caso existam os ficheiros dos outros processos
532
535 Inicializar();
536 printf("\n═╧═ %-2s Fim do Teste (%-2s%d %-2s%s) ═══",
537 Icon(EIcon::FIM), Icon(EIcon::PROCESSO), mpiID,
538 Icon(EIcon::TEMPO), *MostraTempo(Cronometro(CONT_TESTE)));
539 fflush(stdout);
540}
541
543 int inst, int brancas, int pretas, TVector<TVector<int>>* torneio)
544{
545 int resultado = -1, njogada = 0;
546 static TString lance;
547 // carregar instância
548 instancia.valor = inst;
549 Inicializar();
550 // jogar ora de brancas ora de pretas, até o jogo terminar
551 while (!SolucaoCompleta()) {
552 ConfiguracaoAtual(configuracoes[njogada % 2 == 0 ? brancas : pretas], GRAVAR);
559 if (njogada % 2 == 0)
560 resultados.Last().tempoBrancas += Cronometro(CONT_ALGORITMO);
561 else
562 resultados.Last().tempoPretas += Cronometro(CONT_ALGORITMO);
563
564 if (solucao != NULL) { // efetuado um lance
568 printf(" %s", *strAcao);
569 njogada++;
570 if (njogada % 2 == 1) // jogada de brancas, colocar o número de jogada (njogada é meia jogada)
571 (lance = "").printf(" %d. %s", (njogada / 2) + 1, *strAcao);
572 else // jogada de pretas
573 (lance = "").printf(" %s", *strAcao);
574 // concatenar ao registo do jogo
575 resultados.Last().jogo += lance;
576 }
577 else {
578 break; // não há lance efetuado
579 resultado = 0; // erro, mas considerar empatado
580 }
581 }
582 resultados.Last().resultado = Pontos(resultado);
583 resultados.Last().nJogadas = njogada;
584
585 // caso o primeiro a jogar pretenda minimizar, então inverter,
586 // já que o positivo/primeiro a jogar é para as brancas a nível de torneio
587 // mesmo que no jogo o primeiro jogador minimize
588 if ((njogada % 2 == 0) == minimizar)
589 resultados.Last().resultado *= -1;
590
591 // jogo terminou, registar resultado
592 if (torneio != NULL)
593 (*torneio)[brancas][pretas] += resultados.Last().resultado;
594 Debug(COMPLETO, false, " 🏆 %s",
595 (resultados.Last().resultado < 0 ? Icon(EIcon::VIT_PRETA) :
596 (resultados.Last().resultado > 0 ? Icon(EIcon::VIT_BRANCA) :
597 Icon(EIcon::EMPATE))));
598
599 // colocar resultado do jogo no final
600 if (!resultados.Last().jogo.Empty()) {
601 (lance = "").printf(" 🏆 %s", (resultados.Last().resultado < 0 ? Icon(EIcon::VIT_PRETA) :
602 (resultados.Last().resultado > 0 ? Icon(EIcon::VIT_BRANCA) :
603 Icon(EIcon::EMPATE))));
604 resultados.Last().jogo += lance;
605 }
606}
607
609{
610#ifdef MPI_ATIVO
611 int dados[6] = { 0, 0, 0, 4,0,0 }; // instância, brancas, pretas
612 int64_t dadosD[3];
613 double esperaTrabalhadores = 0, esperaGestor = 0;
614 TVector<double> terminou; // instante em que terminou cada trabalhador
616 TVector<int> atual;
617 double periodoReporte = 60;
618
619 TesteInicio(instancias, atual);
620
621 switch (Parametro(NIVEL_DEBUG)) {
622 case DETALHE: periodoReporte = 10; break;
623 case COMPLETO: periodoReporte = 0; break;
624 }
625 for (int i = 1; i < mpiCount; i++)
626 trabalhador += i;
627
628 terminou.Count(mpiCount);
629 terminou.Reset(0);
630
631 // Ciclo:
632 // 1. Enviar trabalho para os escravos
633 // 2. Encerrar escravos a mais
634 // 3. Receber resultados e repetir 1 ou 2 conforme as necessidades
635
636 TVector<TResultadoJogo> resultados; // guarda as soluções obtidas
637 TVector<TResultadoJogo> tarefas; // guarda informação apenas das tarefas a realizar (sem resultados)
638 Cronometro(CONT_REPORTE, true); // reiniciar cronómetro evento
639
640 // construir todas as tarefas
641 // dois jogadores, brancas é o primeiro a jogar, pretas é o segundo
642 for (int brancas = 0; brancas < configuracoes.Count(); brancas++)
643 for (int pretas = 0; pretas < configuracoes.Count(); pretas++)
644 if (brancas != pretas)
645 for (auto inst : instancias)
646 tarefas += { inst, brancas, pretas };
647
648
649 int totalTarefas = tarefas.Count();
650 Debug(ATIVIDADE, false, "\n ├─ %-2sTarefas:%d %-2sInstâncias: %d %-2sConfigurações: %d %-2sProcessos: %d.",
651 Icon(EIcon::TAREFA), tarefas.Count(),
652 Icon(EIcon::INST), instancias.Count(),
653 Icon(EIcon::CONF), configuracoes.Count(),
654 Icon(EIcon::PROCESSO), trabalhador.Count() + 1) &&
655 fflush(stdout);
656
657 // dar uma tarefa a cada escravo
658 while (!tarefas.Empty() && !trabalhador.Empty()) {
659 auto tarefa = tarefas.Pop();
660 dados[0] = tarefa.instancia;
661 dados[1] = tarefa.brancas;
662 dados[2] = tarefa.pretas;
663 trabalhar += trabalhador.Last();
665 esperaTrabalhadores += Cronometro(CONT_TESTE); // estava parado até esta altura
666 }
667 // caso existam escravos sem trabalho, mandar fechar todos, não há mais tarefas
668 dados[0] = dados[1] = -1;
669 while (!trabalhador.Empty()) {
670 auto trabalhadorID = trabalhador.Pop();
673 }
674
675 // receber resultados e continuar a dar trabalho caso exista
676 while (!trabalhar.Empty()) {
678
682 resultados += {dados[0], dados[1], dados[2], dados[3], dados[4]};
683 trabalhar -= stat.MPI_SOURCE;
684 trabalhador += stat.MPI_SOURCE;
685 resultados.Last().jogo.Count(dados[5] + 1);
686 MPI_Recv(resultados.Last().jogo.Data(), dados[5], MPI_CHAR,
690 // tempo de brancas e pretas e de espera do trabalhador (em segundos)
691 resultados.Last().tempoBrancas = (double)dadosD[0] / 1000.;
692 resultados.Last().tempoPretas = (double)dadosD[1] / 1000.;
693 esperaTrabalhadores += (double)((int64_t)dadosD[2]) / 1000.;
694
696 // mostrar uma linha por cada execução
697 Debug(ATIVIDADE, false,
698 "\n ├─ %-2s%-15s %-2s%-5d %-2s%-5d %-2s%-5d %-2s%-5d %-2s%-5d %-2s ",
699 Icon(EIcon::TEMPO), *MostraTempo(Cronometro(CONT_TESTE)),
700 Icon(EIcon::TAREFA), totalTarefas - tarefas.Count(),
701 Icon(EIcon::INST), resultados.Last().instancia,
702 Icon(EIcon::CONF), resultados.Last().brancas,
703 Icon(EIcon::CONF), resultados.Last().pretas,
704 Icon(EIcon::PROCESSO), trabalhador.Last(),
705 Icon(EIcon::IND));
706 fflush(stdout);
708 }
709
710 // ainda há tarefas
711 if (!tarefas.Empty()) {
712 auto tarefa = tarefas.Pop();
713 dados[0] = tarefa.instancia;
714 dados[1] = tarefa.brancas;
715 dados[2] = tarefa.pretas;
716 trabalhar += trabalhador.Last();
718 }
719 else { // tudo feito, mandar sair
720 dados[0] = dados[1] = -1;
721 auto trabalhadorID = trabalhador.Pop();
724 }
725 }
726
727 // contar a espera dos trabalhadores, após terminarem
728 for (int i = 1; i < mpiCount; i++)
730
731 // escrever o ficheiro de resultados
732 int backupCount = mpiCount;
736 mpiCount = 1; // forçar a escrita do ficheiro apenas neste processo
738 Debug(ATIVIDADE, false,
739 "\n ├─ %-2s Ficheiro %s.csv gravado.\n"
740 " │ %-2s Tempo real: %s",
741 Icon(EIcon::RESULT), *ficheiro,
742 Icon(EIcon::TEMPO), *MostraTempo(Cronometro(CONT_TESTE))) &&
743 Debug(ATIVIDADE, false, "\n │ %-2s CPU total: %s",
744 Icon(EIcon::TEMPO), *MostraTempo(Cronometro(CONT_TESTE) * (backupCount - 1))) &&
745 Debug(ATIVIDADE, false, "\n │ %-2s Espera do gestor: %s",
746 Icon(EIcon::TEMPO), *MostraTempo(esperaGestor)) &&
747 Debug(ATIVIDADE, false, "\n │ %-2s Espera trabalhadores: %s",
748 Icon(EIcon::TEMPO), *MostraTempo(esperaTrabalhadores)) &&
749 Debug(ATIVIDADE, false, "\n │ %-2s Utilização:\n │ - Total: %.1f%%\n │ - Gestor: %.1f%%\n │ - Trabalhadores: %.1f%% ",
750 Icon(EIcon::TAXA), taxaUtilizacao * 100, taxaUtilizacaoG * 100, taxaUtilizacaoT * 100);
752
753 TesteFim();
754
755#endif
756}
757
759{
760#ifdef MPI_ATIVO
761 int dados[6] = { 0, 0, 0, 0, 0, 0 }; // instância, brancas, pretas
762 int64_t dadosD[3];
763 // Ciclo:
764 // 1. Solicitar tarefa ao mestre
765 // 2. Executar tarefa
766 // 3. Enviar resultados ao mestre
767 // 4. Repetir até receber ordem de paragem
768
769 TVector<TResultadoJogo> resultados; // guarda resultados dos jogos
770 TVector<int> atual;
771
772 TesteInicio(instancias, atual);
773
774 for (;;) {
775 // receber nova tarefa
777 if (dados[0] < 0)
778 break;
779
780 resultados += {dados[0], dados[1], dados[2]};
782
783 // enviar registo para master, e apagar
784 // dados[0], dados[1] e dados[2] já têm as configurações e instância
785 dados[3] = resultados.Last().resultado;
786 dados[4] = resultados.Last().nJogadas;
787 dados[5] = strlen(resultados.Last().jogo) + 1;
791 // colocar a espera e tempos de brancas e pretas em milissegundos
792 dadosD[0] = (int64_t)(resultados.Last().tempoBrancas * 1000 + 0.5);
793 dadosD[1] = (int64_t)(resultados.Last().tempoPretas * 1000 + 0.5);
794 dadosD[2] = (int64_t)((Cronometro(CONT_TESTE) - inicioEspera) * 1000 + 0.5);
796
797 resultados.Pop();
798 }
799 TesteFim();
800#endif
801}
802
804 TVector<int> referencias, TString fichSolucoes, TString fichResultados)
805{
808 fichSolucoes.printf(".csv");
809 fichResultados.printf(".csv");
810 instancia.valor = instancias.First(); // utilizar uma única instância
811 Inicializar();
813 // ler conteúdo dos ficheiros
814 jogosAtual = fichResultados.readLines();
815 jogosAnterior = fichSolucoes.readLines(); // utilizar para validação
816 // em cada linha está um jogo, com a sequência de jogadas e se terminado o resultado final
817 if (jogosAtual.Empty()) {
818 // ainda não há jogos, criar match
819 for (int i = 0; i < configuracoes.Count(); i++) {
820 // inicia jogo de brancas com configuração i
821 jogosAtual += Jogar("", i);
822 // inicia jogo de pretas com configuração i
823 jogosAtual += TString("");
824 }
825 }
826 else {
828 // Parar o teste e dar pontuação 0
829#ifdef VPL_ATIVO
830 printf("\nComment :=>> histórico do jogo alterado.\nGrade :=>> 0\n");
831#endif
832 fichResultados.writeLines(jogosAnterior); // parar o script global, anterior e atual ficam iguais
833 }
834 else {
835 // jogar em todos os jogos, com as configurações existentes (dois jogos por configuração)
836 for (int i = 0; i < jogosAtual.Count(); i++)
837 jogosAtual[i] = Jogar(jogosAtual[i], (i / 2) % (configuracoes.Count()));
838 }
839 }
840 if (jogosAtual == jogosAnterior) {
841#ifdef VPL_ATIVO
843#endif
844 }
845 else
846 fichResultados.writeLines(jogosAtual);
848}
849
851{
852 // procurar executar o jogo, se todas as jogadas forem válidas, validar
853 int nAcoes = 0;
854 // carregar instância
855 Inicializar();
856 // aplicar todas as ações
857 for (auto acao : solucao) {
858 if (!Acao(acao)) {
859 if (Debug(PASSOS, false,
860 "\n │ Ação inválida: %s (%d ações válidas)", *acao, nAcoes))
861 Debug();
862 return false;
863 }
864 nAcoes++;
865 // jogo terminado
866 if (SolucaoCompleta())
867 return true;
868 }
869 return true;
870}
871
873{
874 TVector<TString> lances = jogo.tok();
875 // não processar jogos terminados
876 if (!lances.Empty()) {
878 for (auto fim : TVector<TString>{ "Empate", "Brancas", "Pretas", "Inválido", "Erro" })
879 if (ultimo == fim)
880 return jogo;
881 }
882 // carregar a configuração correta
885 if (Validar(lances)) {
886 // jogo já terminou
887 if (SolucaoCompleta()) {
888 JogoTerminado(jogo);
889 return jogo;
890 }
891 // executa um lance
894 if (solucao != NULL) { // efetuado um lance
895 jogo.printf(" %s", *Acao(solucao));
897 JogoTerminado(jogo);
898 }
899 else
900 jogo += TString(" Erro");
901 }
902 else
903 jogo += TString(" Inválido");
904 return jogo;
905}
906
908{
909 switch (resultadoCompleto) {
910 case 0: jogo += TString(" Empate"); break;
911 case 1: jogo += TString(" Brancas"); break;
912 case -1: jogo += TString(" Pretas"); break;
913 }
914}
915
916/* VPL_ATIVO
917Critérios de correção (0-100 pontos):
918
919A - resultados - somar para cada jogo o resultado: vitória=2, empate=1, derrota=0, inválido=-1: Intervalo: 0-20 pontos 
920B - número de jogadas no jogo mais longo
921C - número de jogadas no jogo mais rápido
922D - vitórias em jogos rápidos (levam menos de (B+C)/2) - vitória esmagadora  (+1%)
923E - derrotas em jogos rápidos - derrota humilhante (-1%)
924F - vitórias em jogos longos (levam menos de (B+C)/2) - vitória suada (-1%)
925G - derrotas em jogos longos - derrota cobrada caro  (+1%)
926
927Pontuação (0-100): A*5 + D - E - F + G 
928
929Caso a pontuação saia do intervalo 0 a 100, utilizar o extremo mais próximo.
930*/
932{
933 int resultados = 0, maxJogadas = 0, minJogadas = 0, jogoMedio = 0, delta = 0, pontos = 0;
934 TVector<TString> bullets = { "①","②","③","④","⑤","⑥","⑦","⑧","⑨","⑩" };
935 // processar jogos
936 Inicializar(); // coloca minimizar no início, se true, Pretas é primeiro a jogar, c.c. é Brancas
937
938 for (auto& jogo : jogos) {
939 int jogadas = jogo.tok().Count();
940 if (maxJogadas == 0 || jogadas < maxJogadas)
942 if (minJogadas == 0 || jogadas > minJogadas)
944 }
946
947 printf("\nComment :=>> Jogos:");
948
949 for (int i = 0; i < jogos.Count(); i++) {
952 bool deBrancas = (i % 2 == 0); // configurações pares de brancas
953 lances.Pop(); // retirar o resultado, ficam apenas lances
954 printf("\nComment :=>> %s ", *bullets[i]);
955 printf(deBrancas ? "%d🖥️ vs 🧍 — " : "🧍 vs %d🖥️ — ", i / 2 + 1);
956
957 for (auto& lance : lances)
958 printf("%s ", *lance);
959 // colocar o resultado da vitória (cor que ganha)
960 if (resultado == TString("Empate"))
961 printf("→ 🟰 ");
962 else if (resultado == TString("Brancas"))
963 printf("→ ☗ ");
964 else if (resultado == TString("Pretas"))
965 printf("→ ☖ ");
966 else if (resultado == TString("Inválido"))
967 printf("→ ⛔ ");
968 else
969 printf("→ ✗ ");
970
971 // colocar o resultado de empate/derrota/vitória do ponto de vista do engine
972 if (resultado == TString("Empate")) {
973 resultados++; // um ponto por cada empate
974 printf("🟨 ");
975 }
976 else if (resultado == TString(minimizar == deBrancas ? "Brancas" : "Pretas")) {
977 resultados += 2; // dois pontos por cada vitória
978 if (lances.Count() < jogoMedio)
979 delta++; // vitória em jogo rápido
980 else
981 delta--; // vitória em jogo longo
982 printf("🟩 ");
983 }
984 else if (resultado == TString(minimizar == deBrancas ? "Pretas" : "Brancas")) {
985 resultados += 0; // 0 pontos por cada derrota
986 if (lances.Count() < jogoMedio)
987 delta--; // derrota em jogo rápido
988 else
989 delta++; // derrota em jogo longo
990 printf("🟥 ");
991 }
992 else { // inválido ou erro, descontar pontos
993 resultados--; // -1 pontos por jogo com lances inválidos
994 }
995 }
996
997 pontos = Dominio(resultados, 0, 20) * 5 + delta;
998 printf("\nComment :=>> \nComment :=>> Resultados:\nComment :=>> ➤ 🏆 A: %d pontos\nComment :=>> ➤ ⚔️ DEFG: %d\n"
999 "Comment :=>> ➤ 💰 Pontuação (0-100): %d\nGrade :=>> %d\n",
1000 resultados, delta, Dominio(pontos, 0, 100), pontos);
1001
1002}
1003
1005{
1007 // verificar se os jogos terminados foram alterados
1008 for (int i = 0; i < anterior.Count(); i++) {
1010 // não processar jogos terminados
1011 if (!lances.Empty()) {
1013 for (auto fim : TVector<TString>{ "Empate", "Brancas", "Pretas", "Inválido", "Erro" })
1014 if (ultimo == fim) {
1015 // jogo terminado, não pode ser alterado
1016 if (jogos[i] != anterior[i]) {
1017#ifdef VPL_ATIVO
1018 printf("\nComment :=>> jogo %d, tendo terminado foi alterado '%s'!='%s'.", i, *anterior[i], *jogos[i]);
1019#endif
1020 return false;
1021 }
1022 jogosTerminados += i;
1023 break;
1024 }
1025 }
1026 }
1027 // jogos não terminados, apenas podem ter mais uma jogada
1028 for (int i = 0; i < anterior.Count(); i++)
1029 if (jogosTerminados.Find(i) < 0) {
1030 int diff = 0;
1032 TVector<TString> lances = jogos[i].tok();
1033 // verificar se a parte inicial é igual
1034 while (lancesAnt.Count() < lances.Count()) {
1035 lances.Pop();
1036 diff++;
1037 }
1038 if (lancesAnt != lances) {
1039#ifdef VPL_ATIVO
1040 printf("\nComment :=>> alterado lances no jogo %d: '%s'!='%s'.", i, *anterior[i], *jogos[i]);
1041#endif
1042 return false; // não é admissível esta situação de troca de jogadas passadas
1043 }
1044 // diferença deve ser 1 ou 2 (neste caso o jogo terá terminado)
1045 if (diff == 0 || diff > 2) {
1046 // não fez uma jogada, colocar inválido
1047 jogos[i] += TString(" Inválido");
1048 }
1049 else if (diff == 2) { // remover o segundo token para garantir que o resultado é colocado por este engine
1050 lances = jogos[i].tok();
1051 jogos[i] = anterior[i];
1052 jogos[i].printf(" %s", *lances[lances.Count() - 2]);
1053 }
1054 else {
1055 // diferença de um, mas garantir que não é um fim-de-jogo
1056 TString ultimo = jogos[i].tok().Last();
1057 for (auto &fim : TVector<TString>{ "Empate", "Brancas", "Pretas", "Inválido", "Erro" })
1058 if (ultimo == fim) {
1059 // jogo terminado de forma inválida (sem lance extra)
1060 jogos[i] = anterior[i];
1061 jogos[i] += TString(" Inválido");
1062 break;
1063 }
1064 }
1065 }
1066
1067 return true;
1068}
1069
1070
1072 TString nome;
1074 if (mpiCount > 1)
1075 nome.printf("%s_%d.csv", ficheiro.tok().First(), mpiID);
1076 else
1077 nome.printf("%s.csv", ficheiro.tok().First());
1078
1079 // Jogador, Adversário, cor, resultado (positivo caso o jogador ganhe, negativo c.c.)
1080 // Nota: cada confronto fica com 2 entradas; se existir várias instâncias, o resultado do confronto é somado
1081 linhas += TString("Instância;Brancas;Pretas;Resultado;Jogadas;TempoBranco;TempoPreto;Jogo");
1082 for (auto& resultado : resultados)
1083 linhas += TString().printf("%d;%d;%d;%d;%d;%.3f;%.3f;%s",
1084 resultado.instancia,
1085 resultado.brancas + 1,
1086 resultado.pretas + 1,
1087 resultado.resultado,
1088 resultado.nJogadas,
1089 resultado.tempoBrancas,
1090 resultado.tempoPretas,
1091 (gravarSolucao ? *resultado.jogo : ""));
1092
1093 linhas += TString("");
1094 // No final, mostrar as configurações
1095 linhas += TString("Jogador;");
1096 for (int i = 0; i < parametro.Count(); i++)
1097 linhas.Last().printf("P%d(%s);", i + 1, *parametro[i].nome);
1098 for (int jogador = 0; jogador < configuracoes.Count(); jogador++) {
1099 linhas += TString().printf("%d;", jogador + 1);
1100 for (int j = 0; j < parametro.Count(); j++)
1101 if (parametro[j].nomeValores.Empty())
1102 linhas.Last().printf("%d;", configuracoes[jogador][j]);
1103 else
1104 linhas.Last().printf("%d:%s;",
1106 *parametro[j].nomeValores[configuracoes[jogador][j] - parametro[j].min]);
1107 }
1108 nome.writeLines(linhas);
1109 return true;
1110}
1111
1112
1114 int resultado = -1;
1115
1116 switch (Parametro(ALGORITMO)) {
1117 case 1: resultado = MiniMax(Dominio(Parametro(LIMITE), 0)); break;
1118 case 2: resultado = MiniMaxAlfaBeta(Dominio(Parametro(LIMITE), 0)); break;
1119 }
1121 if (Parametro(ORDENAR_SUCESSORES) == 2) {
1124 float taxa = (float)(1.0 * reutilizadoAvaliacao / colocadosHT);
1125 LimparHT();
1126 printf(" HT: reutilização %.2f vezes ", taxa);
1127 }
1128 else
1129 LimparHT();
1132 }
1133 return resultado;
1134}
1135
1136// necessário redefinir para atualizar valorHT em caso de colisão
1139 valorHT[indiceHT = indice].nivel = -1;
1140}
1141
1143 // caso não exista, retorna falso e insere
1144 // se existe retorna falso à mesma para não remover, dado que é uma alternativa para este lance concreto
1145 // se estiver lá outro elemento, substitui
1146 unsigned int original = Hash();
1147 unsigned int indice = original % TAMANHO_HASHTABLE;
1148 indiceHT = indice;
1149 if (elementosHT[indice] != estadoCodHT) {
1150 SubstituirHT(indice);
1151 colocadosHT++;
1152 return false; // não existia
1153 }
1154 return false; // é como se não existisse, mas está lá
1155}
1156
1157
1158bool TProcuraAdversa::ValorEstado(TValorEstado& valor, int operacao) {
1159 if (Parametro(ORDENAR_SUCESSORES) != 2)
1160 return false;
1161 ExisteHT(); // calcula indiceHT
1162 if (operacao == LER) {
1163 if (indiceHT >= 0 && valorHT[indiceHT].nivel >= 0) {
1164 valor = valorHT[indiceHT];
1166 return true;
1167 }
1168 return false;
1169 }
1170 // gravar
1171 if (indiceHT >= 0 && valorHT[indiceHT].nivel < valor.nivel)
1172 valorHT[indiceHT] = valor;
1173
1174 return true;
1175}
1176
1177
1178
@ HEUR_BASE
valor base para diferença entre ameaças de K e K-1 (100 não há diferença, 200 é o valor por omissão)
@ PODA_HEURISTICA
permite cortar sucessores, mas calcula a heurística a todos, de modo a mantendo os melhores
@ ORDENAR_SUCESSORES
opção de ordenar sucessores por heurística, ou por último valor registado
@ PODA_CEGA
corta os sucessores, mesmo sem calcular a heurística, por ordem aleatória
@ EXATO
o valor foi calculado sem cortes, ou seja, não sofreu influência de alfa ou beta;
@ LOWER_BOUND
o valor foi afetado por um corte de beta (ou seja, ele é pelo menos esse valor, mas pode ser maior);
@ UPPER_BOUND
o valor foi afetado por um corte de alfa (ou seja, ele é no máximo esse valor, mas pode ser menor).
@ LIMITE
Valor dependente do algoritmo. Exemplo: Profundidade limitada.
@ BARALHAR_SUCESSORES
Baralhar os sucessores ao expandir.
@ RUIDO_HEURISTICA
Ruído a adicionar à heurística para testes de robustez.
@ ESTADOS_REPETIDOS
Forma de lidar com estados repetidos (ignorá-los, ascendentes, gerados).
@ IGNORADOS
ignorados os estados gerados repetidos
@ GERADOS
estados são comparados com todos os gerados, e se forem repetidos são removidos
constexpr int TAMANHO_HASHTABLE
@ TAG_TRABALHO
Definition TProcura.h:125
@ TAG_JOGO
Definition TProcura.h:128
@ TAG_VALORES
Definition TProcura.h:127
@ TAG_CABECALHO
Definition TProcura.h:126
@ GRAVAR
Definition TProcura.h:104
@ LER
Definition TProcura.h:104
@ PASSOS
Exibe passos intermediários.
Definition TProcura.h:93
@ ATIVIDADE
Apenas eventos principais.
Definition TProcura.h:92
@ COMPLETO
Mostra toda a execução detalhadamente.
Definition TProcura.h:95
@ NADA
Sem informações de debug.
Definition TProcura.h:91
@ DETALHE
Debug detalhada sobre estados e decisões.
Definition TProcura.h:94
@ CONT_TESTE
Tempo total do teste (todas as execuções)
Definition TProcura.h:113
@ CONT_REPORTE
Tempo entre mensagens durante o teste.
Definition TProcura.h:114
@ CONT_ALGORITMO
Tempo da execução do algoritmo por instância.
Definition TProcura.h:112
@ ALGORITMO
Algoritmo base a executar.
Definition TProcura.h:70
@ SEMENTE
Semente aleatória para inicializar a sequência de números pseudo-aleatórios.
Definition TProcura.h:72
@ NIVEL_DEBUG
Nível de debug, de reduzido a completo.
Definition TProcura.h:71
Representa um estado no espaço de estados.
int NoFolha(bool nivel)
fim da procura, por corte de nível (ou não haver sucessores), retornar heurística
void PontuacaoJogos(TVector< TString > &jogos)
bool Validar(TVector< TString > solucao) override
Verifica a validade de uma solução para a instância atual.
bool SolucaoCompleta(void)=0
Verifica se o estado actual é objectivo (é uma solução completa)
int Pontos(int resultado)
converte -infinito, 0, +infinito em -1 (vitória preta), 1 (vitória branca), 0 empate
void OrdenarSucessores(TVector< TNo > &sucessores, TVector< int > &id, int nivel)
void TesteValidacao(TVector< int > instancias, TVector< int > impossiveis, TVector< int > referencias, TString fichSolucoes, TString fichResultados="") override
Executa testes de validação, executando cada solução na instância respetiva, e verificando a sua vali...
void TesteEmpiricoTrabalhador(TVector< int > instancias, TString ficheiro="") override
Teste empírico com modo mestre-escravo (este é o escravo)
int MiniMax(int nivel=4)
retorna o valor do estado actual, apos procura de profundidade nivel
int MaiorAmeaca(TVector< int > &qMin, TVector< int > &qMax, int maxAmeaca) const
Utilitário para calculo de uma heurística standard em jogos simples.
void Sucessores(TVector< TNo > &sucessores)
Coloca em sucessores a lista de estados sucessores.
static bool completo
controlo para indicar se a procura foi realizada de forma completa (c.c. foi cortada)
bool minimizar
o jogador actual deve minimizar o custo (ou maximizar caso tenha o valor falso)
int Heuristica(void)
chamar após calcular a heurística (grava o valor, dependendo da parametrização)
int MetodoIterativo(int alfaBeta)
iteração, aumentando o nível progressivamente
void TesteEmpirico(TVector< int > instancias, TString ficheiro="") override
Executa testes empíricos, em todas as configurações guardadas, nas instâncias selecionadas.
bool CorteAlfaBeta(int valor, int &alfa, int &beta)
verifica se há um corte alfa/beta, atualizando alfa e beta
bool RelatorioCSV(TVector< TResultadoJogo > &resultados, TString ficheiro)
Gera um relatório CSV com os resultados.
void JogoTerminado(TString &jogo)
bool ValorEstado(TValorEstado &valor, int operacao)
ler ou gravar o melhor valor conhecido
bool ExisteHeuritica(void)
void ExecutaTarefa(TVector< TResultadoJogo > &resultados, int inst, int brancas, int pretas, TVector< TVector< int > > *torneio=NULL)
Executa uma tarefa num teste empírico.
void ResetParametros()
Método para inicializar os parâmetros (redefinir se forem adicionados parâmetros específicos)
void SubstituirHT(int indice)
static int reutilizadoAvaliacao
bool Utilizavel(TValorEstado &valor, int nivel, int alfa, int beta)
ver se o valor obtido é utilizável no contexto atual
void TesteEmpiricoGestor(TVector< int > instancias, TString ficheiro="") override
Teste empírico com modo mestre-escravo (este é o mestre)
static int infinito
valor de infinito (vitoria/derrota), omissao 1000
TString Jogar(TString jogo, int configID)
static int nivelOK
profundidade máxima no método iterativo
void DebugChamada(bool noFolha, int alfa=0, int beta=0)
int MiniMaxAlfaBeta(int nivel=4, int alfa=-infinito, int beta=+infinito)
retorna o valor do estado actual, apos procura de profundidade nivel. Idêntico a MiniMax
static TValorEstado valorHT[TAMANHO_HASHTABLE]
static int resultadoCompleto
resultado após SolucaoCompleta() retornar true (-1 vitória minimizar, 0 empate, 1 vitória maximizar)
bool CoerenciaJogo(TVector< TString > &jogos, TVector< TString > &anterior)
int ExecutaAlgoritmo()
Executa o algoritmo com os parametros atuais.
Representa um estado no espaço de estados.
void DebugCorte(int sucessores=-1, bool duplo=false)
void DebugEstado(bool novaLinha=true) const
void DebugFolha(bool fimLinha, const char *fmt,...)
static TBits elementosHT[TAMANHO_HASHTABLE]
void CalcularHeuristicas(TVector< TNo > &sucessores, TVector< int > *id=NULL, bool sortLB=false)
void NovaLinha(bool tudo=true)
static void LibertarVector(TVector< TNo > &vector, int excepto=-1, int maiorQue=-1)
virtual void SubstituirHT(int indice)
void DebugExpansao(int sucessor, int sucessores, bool minimizar=true)
void DebugIteracao(int iteracao, const char *simbolo)
void LimparEstatisticas() override
Chamar antes da execução do algoritmo. Limpa valores estatísticos, e fixa o instante limite de tempo ...
virtual void Debug(bool completo=true)
Mostra o estado no ecrã, para debug.
Definition TProcura.cpp:86
int Parametro(int id) const
Definition TProcura.h:607
static int Dominio(int &variavel, int min=INT_MIN, int max=INT_MAX)
Limita o domínio de um parâmetro inteiro.
static int modoMPI
Modo MPI.
Definition TProcura.h:590
static TVector< TVector< int > > configuracoes
Conjuntos de configurações para teste empírico.
Definition TProcura.h:573
static int resultado
Resultado retornado pelo algoritmo na última execução.
Definition TProcura.h:575
void MostrarTorneio(TVector< TVector< int > > &torneio, bool jogo=false)
Mostra os resultados do torneio.
static int mpiID
MPI - rank do processo.
Definition TProcura.h:585
void MostrarConfiguracoes(int detalhe, int atual=-1)
Mostra as configurações disponíveis.
Definition TProcura.cpp:934
static int gravarSolucao
Gravar solução CSV (todas as ações)
Definition TProcura.h:593
void TesteInicio(TVector< int > &instancias, TVector< int > &configAtual)
arranque de teste, auxiliar aos Testes Empíricos
Definition TProcura.cpp:987
virtual bool Parar(void)
Verifica se a procura deve ser interrompida.
Definition TProcura.h:405
bool JuntarCSV(TString ficheiro)
Juntar ficheiros CSV gerados por diferentes processos MPI em um único ficheiro.
static TString MostraTempo(double segundos)
Mostra tempo num formato humano.
Definition TProcura.cpp:752
static int mpiCount
MPI - número de processos.
Definition TProcura.h:587
void TesteFim()
void ConfiguracaoAtual(TVector< int > &parametros, int operacao)
Grava ou lê a configuração atual.
Definition TProcura.cpp:740
static TParametro instancia
ID da instância atual, a ser utilizado em SolucaoVazia().
Definition TProcura.h:22
static double Cronometro(enum ECronometro id=CONT_ALGORITMO, bool inicializar=false)
retorna o tempo em segundos desde que o cronómetro foi inicializado
Definition TProcura.h:854
static TVector< TParametro > parametro
Parâmetros a serem utilizados na configuração atual.
Definition TProcura.h:567
static void MostraConjunto(TVector< int > valores, const char *etiqueta)
Definition TProcura.cpp:969
TVector< TString > tok(const char *delim=" \t\n\r") const
Definition TVector.h:1196
TString & printf(const char *fmt,...)
Definition TVector.h:1150
TString & writeLines(const TVector< TString > &lines, bool append=false)
Definition TVector.h:1248
Item & First()
Definition TVector.h:224
Item & Pop()
Definition TVector.h:193
Item & Last()
Definition TVector.h:227
int Count() const
Definition TVector.h:230
virtual bool Empty() const
Definition TVector.h:233
TVector< Item > & Push(Item a)
Definition TVector.h:190
virtual void Sucessores(TVector< TNo > &sucessores)
Coloca em sucessores a lista de estados sucessores.
virtual bool SolucaoCompleta(void)
Verifica se o estado actual é objectivo (é uma solução completa)
void Inicializar(void) override
Coloca o objecto no estado inicial da procura.
void ResetParametros() override
Redefinição. Ver TProcura::ResetParametros().
virtual void Copiar(TNo objecto)
Fica com uma cópia do objecto.
virtual int Heuristica(void)
Função para calcular quanto falta para o final, o valor da heurística.
virtual TString Acao(TNo sucessor)
Retorna a ação (movimento, passo, jogada, lance, etc.) que gerou o sucessor.
int heuristica
Estimativa para o custo até um estado objetivo, se disponível.
int custo
Custo total acumulado desde o estado inicial.
TNo pai
Ponteiro para o estado pai, na árvore de procura.
int debugID
ID do estado, para efeitos de debug.
static constexpr const char * RAMO_ESTADO
static int expansoes
Número de expansões efetuadas.
static constexpr const char * RAMO_ESTADO2
static TVector< const char * > ramo
static TNo solucao
Estado objetivo encontrado, retornado pela procura (deve ser libertado).
static constexpr const char * RAMO_CONTINUA
static constexpr const char * RAMO_VAZIO
unsigned int rand(int seq)
Retorna o próximo valor pseudo-aleatório.
Definition TRand.cpp:46
void srand(unsigned int seed, int seq)
Inicializa a semente da geração pseudo-aleatória.
Definition TRand.cpp:34
int valor
valor do parâmetro
Definition TProcura.h:179
registo do valor de um estado, em procuras anteriores
ETipoValor tipo