Jogo de Damas em WLanguage e oop
Iniciado por Boller, 17,mar. 2025 18:09 - No hay respuesta
Miembro registrado 3.855 mensajes
Publicado el 17,marzo 2025 - 18:09
Boa tarde Vou criar um exemplo de jogo de damas (checkers) em WLanguage utilizando programação orientada a objetos (OOP) e incluindo uma inteligência artificial (IA) básica baseada em Minimax com poda Alfa-Beta. O jogo seguirá as regras padrão das damas internacionais (tabuleiro 10x10, capturas obrigatórias, damas com movimento longo), mas pode ser adaptado para variantes como damas brasileiras (8x8) se desejado. Vou estruturar o código em classes, lógica de jogo e IA, aproveitando os recursos da linguagem. --- ### Código do Jogo de Damas em WLanguage #### Classe Base: Peça ```windev CLASS Peça PUBLIC sCor IS string // "Branco" ou "Preto" nPosicao IS int // Posição no tabuleiro (1 a 50 para 10x10) bDama IS boolean bAtiva IS boolean PROCEDURE Construtor(sCorPeça IS string, nPos IS int) sCor = sCorPeça nPosicao = nPos bDama = False bAtiva = True PROCEDURE VIRTUAL MovimentosPossíveis() RETURNS array of string RETURN [] PROCEDURE VIRTUAL CapturasPossíveis() RETURNS array of string RETURN [] END ``` #### Classe Peça de Damas ```windev CLASS PecaDamas INHERITS Peça PROCEDURE MovimentosPossíveis() RETURNS array of string aMovimentos IS array of string nLinha IS int = (nPosicao - 1) / 5 + 1 nColuna IS int = Mod(nPosicao - 1, 5) + 1 nDireção IS int = IIF(sCor = "Branco", -1, 1) IF NOT bDama THEN // Movimentos simples IF Tabuleiro::PosicaoValida(nLinha + nDireção, nColuna - 1) AND Tabuleiro::PosicaoLivre(Tabuleiro::PosicaoParaNumero(nLinha + nDireção, nColuna - 1)) THEN ArrayAdd(aMovimentos, NumToString(Tabuleiro::PosicaoParaNumero(nLinha + nDireção, nColuna - 1))) END IF Tabuleiro::PosicaoValida(nLinha + nDireção, nColuna + 1) AND Tabuleiro::PosicaoLivre(Tabuleiro::PosicaoParaNumero(nLinha + nDireção, nColuna + 1)) THEN ArrayAdd(aMovimentos, NumToString(Tabuleiro::PosicaoParaNumero(nLinha + nDireção, nColuna + 1))) END ELSE // Movimentos de dama (diagonais longas) FOR nDist = 1 TO 9 IF Tabuleiro::PosicaoValida(nLinha + nDist, nColuna + nDist) AND Tabuleiro::PosicaoLivre(Tabuleiro::PosicaoParaNumero(nLinha + nDist, nColuna + nDist)) THEN ArrayAdd(aMovimentos, NumToString(Tabuleiro::PosicaoParaNumero(nLinha + nDist, nColuna + nDist))) ELSE BREAK END END // Outras diagonais (esquerda-cima, direita-baixo, etc.) seguem lógica similar END RETURN aMovimentos PROCEDURE CapturasPossíveis() RETURNS array of string aCapturas IS array of string nLinha IS int = (nPosicao - 1) / 5 + 1 nColuna IS int = Mod(nPosicao - 1, 5) + 1 nDireção IS int = IIF(sCor = "Branco", -1, 1) IF NOT bDama THEN // Captura simples IF Tabuleiro::PodeCapturar(nLinha, nColuna, nLinha + 2*nDireção, nColuna - 2, sCor) THEN ArrayAdd(aCapturas, NumToString(Tabuleiro::PosicaoParaNumero(nLinha + 2*nDireção, nColuna - 2))) END IF Tabuleiro::PodeCapturar(nLinha, nColuna, nLinha + 2*nDireção, nColuna + 2, sCor) THEN ArrayAdd(aCapturas, NumToString(Tabuleiro::PosicaoParaNumero(nLinha + 2*nDireção, nColuna + 2))) END ELSE // Captura longa de dama FOR nDist = 1 TO 9 IF Tabuleiro::PodeCapturarLonga(nLinha, nColuna, nLinha + nDist, nColuna + nDist, sCor) THEN ArrayAdd(aCapturas, NumToString(Tabuleiro::PosicaoParaNumero(nLinha + nDist, nColuna + nDist))) BREAK END END // Verificar outras diagonais END RETURN aCapturas END ``` #### Classe Tabuleiro ```windev CLASS Tabuleiro PUBLIC aPeças IS array of PecaDamas sJogadorAtual IS string = "Branco" PROCEDURE PosicaoParaNumero(nLin IS int, nCol IS int) RETURNS int RETURN (nLin - 1) * 5 + nCol PROCEDURE NumeroParaPosicao(nPos IS int, OUT nLin IS int, OUT nCol IS int) nLin = (nPos - 1) / 5 + 1 nCol = Mod(nPos - 1, 5) + 1 PROCEDURE PosicaoValida(nLin IS int, nCol IS int) RETURNS boolean RETURN nLin >= 1 AND nLin <= 10 AND nCol >= 1 AND nCol <= 5 AND Mod(nLin + nCol, 2) = 0 // Apenas casas escuras PROCEDURE PosicaoLivre(nPos IS int) RETURNS boolean FOR EACH p OF aPeças IF p::nPosicao = nPos AND p::bAtiva THEN RETURN False END END RETURN True PROCEDURE PodeCapturar(nLinOrig IS int, nColOrig IS int, nLinDest IS int, nColDest IS int, sCor IS string) RETURNS boolean nLinMeio IS int = (nLinOrig + nLinDest) / 2 nColMeio IS int = (nColOrig + nColDest) / 2 nPosMeio IS int = PosicaoParaNumero(nLinMeio, nColMeio) nPosDest IS int = PosicaoParaNumero(nLinDest, nColDest) IF PosicaoValida(nLinDest, nColDest) AND PosicaoLivre(nPosDest) THEN FOR EACH p OF aPeças IF p::nPosicao = nPosMeio AND p::bAtiva AND p::sCor <> sCor THEN RETURN True END END END RETURN False PROCEDURE PodeCapturarLonga(nLinOrig IS int, nColOrig IS int, nLinDest IS int, nColDest IS int, sCor IS string) RETURNS boolean // Lógica para dama: verificar peça adversária no caminho e destino livre RETURN False // Implementar conforme necessário PROCEDURE Inicializar() // Peças brancas (posições 1 a 20) FOR i = 1 TO 20 ArrayAdd(aPeças, PecaDamas("Branco", i)) END // Peças pretas (posições 31 a 50) FOR i = 31 TO 50 ArrayAdd(aPeças, PecaDamas("Preto", i)) END PROCEDURE MoverPeça(pPeça IS PecaDamas, nNovaPosicao IS int) RETURNS boolean aCapturas IS array of string = pPeça::CapturasPossíveis() aMovimentos IS array of string = pPeça::MovimentosPossíveis() IF ArrayCount(aCapturas) > 0 THEN IF ArraySearch(aCapturas, NumToString(nNovaPosicao)) >= 0 THEN ExecutarCaptura(pPeça, nNovaPosicao) RETURN True END ELSE IF ArraySearch(aMovimentos, NumToString(nNovaPosicao)) >= 0 THEN pPeça::nPosicao = nNovaPosicao VerificarPromocao(pPeça) sJogadorAtual = IIF(sJogadorAtual = "Branco", "Preto", "Branco") RETURN True END RETURN False PROCEDURE ExecutarCaptura(pPeça IS PecaDamas, nNovaPosicao IS int) nLinOrig IS int, nColOrig IS int nLinDest IS int, nColDest IS int NumeroParaPosicao(pPeça::nPosicao, nLinOrig, nColOrig) NumeroParaPosicao(nNovaPosicao, nLinDest, nColDest) nLinMeio IS int = (nLinOrig + nLinDest) / 2 nColMeio IS int = (nColOrig + nColDest) / 2 nPosMeio IS int = PosicaoParaNumero(nLinMeio, nColMeio) FOR EACH p OF aPeças IF p::nPosicao = nPosMeio AND p::bAtiva THEN p::bAtiva = False BREAK END END pPeça::nPosicao = nNovaPosicao VerificarPromocao(pPeça) sJogadorAtual = IIF(sJogadorAtual = "Branco", "Preto", "Branco") PROCEDURE VerificarPromocao(pPeça IS PecaDamas) nLinha IS int = (pPeça::nPosicao - 1) / 5 + 1 IF (pPeça::sCor = "Branco" AND nLinha = 1) OR (pPeça::sCor = "Preto" AND nLinha = 10) THEN pPeça::bDama = True END END ``` #### Inteligência Artificial (Minimax com Alfa-Beta) ```windev PROCEDURE AvaliarPosicao() RETURNS int nPontos IS int = 0 FOR EACH p OF Tab::aPeças IF p::bAtiva THEN nValor IS int = IIF(p::bDama, 3, 1) nPontos += IIF(p::sCor = "Branco", nValor, -nValor) END END RETURN nPontos PROCEDURE AlfaBeta(nProfundidade IS int, nAlfa IS int, nBeta IS int, bMaximizando IS boolean) RETURNS int IF nProfundidade = 0 OR Tab::VerificarFimJogo() <> "" THEN RETURN AvaliarPosicao() END IF bMaximizando THEN nValor IS int = -Infinity FOR EACH p OF Tab::aPeças IF p::sCor = Tab::sJogadorAtual AND p::bAtiva THEN aMov IS array of string = p::CapturasPossíveis() IF ArrayCount(aMov) = 0 THEN aMov = p::MovimentosPossíveis() FOR EACH sMov OF aMov SalvarEstado() Tab::MoverPeça(p, Val(sMov)) nValor = Max(nValor, AlfaBeta(nProfundidade - 1, nAlfa, nBeta, False)) RestaurarEstado() nAlfa = Max(nAlfa, nValor) IF nBeta <= nAlfa THEN BREAK END END END RETURN nValor ELSE nValor IS int = Infinity // Similar, mas minimizando END PROCEDURE CalcularMelhorJogada(nProfundidade IS int) RETURNS string sMelhorJogada IS string nMelhorValor IS int = -Infinity FOR EACH p OF Tab::aPeças IF p::sCor = Tab::sJogadorAtual AND p::bAtiva THEN aMov IS array of string = p::CapturasPossíveis() IF ArrayCount(aMov) = 0 THEN aMov = p::MovimentosPossíveis() FOR EACH sMov OF aMov SalvarEstado() Tab::MoverPeça(p, Val(sMov)) nValor IS int = AlfaBeta(nProfundidade - 1, -Infinity, Infinity, False) RestaurarEstado() IF nValor > nMelhorValor THEN nMelhorValor = nValor sMelhorJogada = NumToString(p::nPosicao) + "-" + sMov END END END END RETURN sMelhorJogada END ``` #### Janela Principal ```windev WINDOW JogoDamas Tab IS Tabuleiro PROCEDURE GLOBAL InicializarJogo() Tab = Tabuleiro() Tab::Inicializar() GRD_Tabuleiro IS Grid GRD_Tabuleiro..Rows = 10 GRD_Tabuleiro..Columns = 5 AtualizarTabuleiro() PROCEDURE AtualizarTabuleiro() FOR i = 1 TO 10 FOR j = 1 TO 5 GRD_Tabuleiro[i,j] = "" END END FOR EACH p OF Tab::aPeças IF p::bAtiva THEN nLin IS int, nCol IS int Tab::NumeroParaPosicao(p::nPosicao, nLin, nCol) GRD_Tabuleiro[nLin, nCol] = IIF(p::bDama, "D", "P") + IIF(p::sCor = "Branco", "B", "P") END END PROCEDURE GRD_Tabuleiro..Clicked() STATIC pSelecionada IS PecaDamas nLin IS int = GRD_Tabuleiro..ClickedRow nCol IS int = GRD_Tabuleiro..ClickedColumn nPos IS int = Tab::PosicaoParaNumero(nLin, nCol) IF pSelecionada = Null THEN FOR EACH p OF Tab::aPeças IF p::nPosicao = nPos AND p::sCor = Tab::sJogadorAtual THEN pSelecionada = p BREAK END END ELSE IF Tab::MoverPeça(pSelecionada, nPos) THEN AtualizarTabuleiro() END pSelecionada = Null END PROCEDURE BTN_JogadaIA..Clicked() sJogada IS string = CalcularMelhorJogada(4) IF sJogada <> "" THEN nPosOrig IS int = Val(ExtractString(sJogada, 1, "-")) nPosDest IS int = Val(ExtractString(sJogada, 2, "-")) FOR EACH p OF Tab::aPeças IF p::nPosicao = nPosOrig THEN Tab::MoverPeça(p, nPosDest) BREAK END END AtualizarTabuleiro() END END ``` --- ### Explicação do Código #### Estrutura OOP - **Peça**: Classe base com propriedades comuns (cor, posição, dama). - **PecaDamas**: Implementa movimentos e capturas, diferenciando entre peças normais e damas. - **Tabuleiro**: Gerencia o estado do jogo, incluindo validação de posições, captura e promoção. #### Lógica do Jogo - **Numeração**: O tabuleiro 10x10 usa posições de 1 a 50, apenas nas casas escuras (32 jogáveis em um tabuleiro 8x8, mas aqui adaptado para 10x10). - **Capturas Obrigatórias**: Se há capturas disponíveis, elas têm prioridade sobre movimentos simples. - **Promoção**: Peças viram damas ao atingir a linha oposta. #### Inteligência Artificial - **Minimax com Alfa-Beta**: Calcula a melhor jogada com profundidade limitada (ex.: 4), otimizando com poda. - **Avaliação**: Pontua com base no número de peças (1 para normal, 3 para dama), favorecendo o jogador atual. - **Execução**: A IA escolhe a jogada com maior valor e a aplica no tabuleiro. #### Interface - **Grid**: Representa o tabuleiro 10x5 (metade das colunas, já que apenas casas escuras são usadas). - **Interação**: Clique para selecionar e mover peças; botão para IA jogar. --- ### Melhorias Possíveis 1. **Capturas Múltiplas**: Permitir capturas encadeadas (não implementado ainda). 2. **Fim de Jogo**: Adicionar verificação de vitória/empate. 3. **IA Avançada**: Incluir heurísticas como controle do centro ou mobilidade. 4. **Visual**: Usar imagens para peças em vez de texto. Se quiser, posso detalhar qualquer parte ou adicionar essas melhorias! O que acha? -- Adriano José Boller ______________________________________________ Consultor e Representante Oficial da PcSoft no Brasil +55 (41) 99949 1800 adrianoboller@gmail.com skype: adrianobollerhttp://wxinformatica.com.br/