PC SOFT

FOROS PROFESIONALES
WINDEVWEBDEV y WINDEV Mobile

Inicio → WINDEV 25 → Jogo de Damas em WLanguage e oop
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: adrianoboller
http://wxinformatica.com.br/