Untitled

 avatar
unknown
plain_text
a month ago
22 kB
1
Indexable
{-|
Module      : Tarefa1
Description : Invariantes do Jogo
Copyright   : Vasco Marques Machado <a109949@alunos.uminho.pt>
              Duarte Emanuel Soares Arruda <a109840@alunos.uminho.pt>


Módulo para a realização da Tarefa 1 de LI1 em 2024/25.
-}
module Tarefa1 where

import LI12425


-- | Função que verifica que todas as funções que se seguem retornam um resultado verdadeiro, validando assim que o jogo reune todas as condições necessárias para funcionar corretamente
validaJogo :: Jogo -> Bool
validaJogo Jogo{portaisJogo = []} = False
validaJogo Jogo{baseJogo = Base{posicaoBase = b, creditosBase = c}, portaisJogo = p@(x:y), mapaJogo = mapa, torresJogo = t, inimigosJogo = i} = validaPortaisBase p b && validaTorreBase t b && validaTorrePortal t p && validaInimigosInativos i p && validaInimigoTerra i mapa && validaTorreInimigo t i && validaInimigosVelocidade i && validaProjeteisInimigos i && validaProjeteisInimigos i && validaTorreRelva t mapa && validaAlcanceTorre t && validaRajadaTorre t && validaCicloTorre t && validaTorresSobrepostas t && validaBaseTerra b mapa && validaCreditoBase c

validaPortais :: Portal -> Mapa -> Posicao -> Bool
validaPortais Portal{posicaoPortal = (x,y)} mapa b = validaCaminhoTerra (x,y) mapa b

validaCaminhoTerra :: Posicao -> Mapa -> Posicao -> Bool
validaCaminhoTerra (x,y) mapa b = (eTerra (x,y) mapa) && (caminhoTerra (x-1,y) mapa b Este || caminhoTerra (x+1,y) mapa b Oeste || caminhoTerra (x,y+1) mapa b Sul || caminhoTerra (x,y-1) mapa b Norte)

caminhoTerra :: Posicao -> Mapa -> Posicao -> Direcao -> Bool
caminhoTerra p@(x,y) mapa b Norte = if p == b then True else if rodeadoTerra p mapa && eTerra p mapa then caminhoTerra (x-1,y) mapa b Este || caminhoTerra (x+1,y) mapa b Oeste || caminhoTerra (x,y+1) mapa b Sul else False
caminhoTerra p@(x,y) mapa b Este = if p == b then True else if rodeadoTerra p mapa && eTerra p mapa then caminhoTerra (x+1,y) mapa b Oeste || caminhoTerra (x,y+1) mapa b Sul || caminhoTerra (x,y-1) mapa b Norte else False
caminhoTerra p@(x,y) mapa b Sul = if p == b then True else if rodeadoTerra p mapa && eTerra p mapa then caminhoTerra (x-1,y) mapa b Este || caminhoTerra (x+1,y) mapa b Oeste || caminhoTerra (x,y-1) mapa b Norte else False
caminhoTerra p@(x,y) mapa b Oeste = if p == b then True else if rodeadoTerra p mapa && eTerra p mapa then caminhoTerra (x-1,y) mapa b Este || caminhoTerra (x,y+1) mapa b Sul || caminhoTerra (x,y-1) mapa b Norte else False

rodeadoTerra :: Posicao -> Mapa -> Bool
rodeadoTerra (x,y) mapa@(h:t) = (length(mapa) > floor y) && (eTerra (x,y+1) mapa) ||
                                (y > 0) && (eTerra (x,y-1) mapa) ||
                                ((length(h) > floor x) && (eTerra (x+1,y) mapa)) ||
                                (x > 0) && (eTerra (x-1,y) mapa)

eTerra :: Posicao -> Mapa -> Bool
eTerra (x,y) mapa = if x < 0 || y < 0 then False
                    else (mapa !! floor y !! floor x) == Terra

-- | Verifica se os portais não estão sobrepostos à base
validaPortaisBase :: [Portal] -> Posicao -> Bool
validaPortaisBase [] _ = True
validaPortaisBase (Portal{posicaoPortal = (p1,p2)} : ps) (b1, b2) | p1 == b1 && p2 == b2 = False
                                                                  | otherwise = validaPortaisBase ps (b1, b2)
-- | Verifica que as torres não estão sobrepostas à base
validaTorreBase :: [Torre] -> Posicao -> Bool
validaTorreBase [] _ = True
validaTorreBase (Torre{posicaoTorre = (t1,t2)} : ts) (b1, b2) | t1 == b1 && t2 == b2 = False
                                                              | otherwise = validaTorreBase ts (b1, b2)
-- | Verifica que as torres não estão sobrepostas aos portais
validaTorrePortal :: [Torre] -> [Portal] -> Bool
validaTorrePortal _ [] = True
validaTorrePortal t (Portal{posicaoPortal = p} : ps) | validaTorrePortalAux t p = validaTorrePortal t ps
                                                     | otherwise = False
-- | Função auxiliar que verifica que as torres não estão sobrepostas a um portal apenas
validaTorrePortalAux :: [Torre] -> Posicao -> Bool
validaTorrePortalAux [] _ = True
validaTorrePortalAux (Torre{posicaoTorre = (t1,t2)} : ts) (p1,p2) | t1 == p1 && t2 == p2 = False
                                                                  | otherwise = validaTorrePortalAux ts (p1,p2) 

-- | Verifica que não existe mais de uma onda ativa por portal
validaOndasAtivas :: [Portal] -> Bool
validaOndasAtivas [] = True
validaOndasAtivas (p:ps) | validaOndasAtivasAux p = validaOndasAtivas ps
                         | otherwise = False

-- | Função auxiliar da função validaOndasAtivas que verifica a mesma condição para cada portal individualmente
validaOndasAtivasAux :: Portal -> Bool
validaOndasAtivasAux Portal{ondasPortal = o} = validaOndasAtivasAc o 0
    where
        validaOndasAtivasAc :: [Onda] -> Int -> Bool
        validaOndasAtivasAc [] 2 = False
        validaOndasAtivasAc [] _ = True
        validaOndasAtivasAc (Onda{entradaOnda = e} : os) ac | ac > 1 = False
                                                            | e == 0 = validaOndasAtivasAc os (ac+1)
                                                            | e /= 0 = validaOndasAtivasAc os ac

-- | Verifica que os inimigos por lançar têm a posição do respetivo portal, nível de vida positivo e lista de projeteis ativos vazia
validaInimigosInativos :: [Inimigo] -> [Portal] -> Bool
validaInimigosInativos [] _ = True
validaInimigosInativos (i : is) p | validaInimigosInativosAux i p = validaInimigosInativos is p
                                  | otherwise = False

-- | Auxiliar da função validaInimigosInativos que verifica para cada inimigo se as condições anteriores estão reunidas
validaInimigosInativosAux :: Inimigo -> [Portal] -> Bool
validaInimigosInativosAux i [] = True
validaInimigosInativosAux i (Portal{posicaoPortal = p, ondasPortal = o} : xs) | validaInimigosOnda i o && (inimigoParaPosicao i) /= p = False
                                                                              | validaInimigosOnda i o && validaVidaProjeteis i == False = False
                                                                              | validaInimigosOnda i o && (inimigoParaPosicao i) == p = True
                                                                              | validaInimigosOnda i o && validaVidaProjeteis i = True
                                                                              | otherwise = validaInimigosInativosAux i xs

-- | Verifica se um enimigo pertence a uma onda (utilizada na função validaInimigosInativos)
validaInimigosOnda :: Inimigo -> [Onda] -> Bool
validaInimigosOnda i [] = False
validaInimigosOnda i (Onda{inimigosOnda = l} : xs) | elem i l = True
                                                   | otherwise = validaInimigosOnda i xs

-- | Recebe um inimigo e retorna a sua posição (facilita a escrita da função validaInimigosInativosAux)
inimigoParaPosicao :: Inimigo -> Posicao
inimigoParaPosicao (Inimigo{posicaoInimigo = x}) = x

-- | Verifica se a vida do inimigo é positiva e se a lista de projeteis ativos se encontra vazia (utilizada como auxiliar da função validaInimigosInativosAux)
validaVidaProjeteis :: Inimigo -> Bool
validaVidaProjeteis (Inimigo{vidaInimigo = v, projeteisInimigo = l}) | v >= 0 && length l == 0 = True
                                                                     | otherwise = False
-- | Verifica que todos os inimigos se encontram sobre terra
validaInimigoTerra :: [Inimigo] -> Mapa -> Bool
validaInimigoTerra [] _ = True
validaInimigoTerra ((Inimigo{posicaoInimigo = (i1,i2)}):xs) mapa | (mapa !! floor i2 !! floor i1) == Terra = validaInimigoTerra xs mapa
                                                                 | otherwise = False
-- | Verifica que as torres não estão sobrepostas aos inimigos
validaTorreInimigo :: [Torre] -> [Inimigo] -> Bool
validaTorreInimigo t [] = True
validaTorreInimigo t ((Inimigo{posicaoInimigo = (p1,p2)}) : ps) | validaTorreInimigoAux t (p1,p2) = validaTorreInimigo t ps
                                                                | otherwise = False
-- | Auxliar da função validaTorreInimigo que verifica para cada inimigo se está sobreposto a alguma torre
validaTorreInimigoAux :: [Torre] -> Posicao -> Bool
validaTorreInimigoAux [] _ = True
validaTorreInimigoAux ((Torre{posicaoTorre = (t1,t2)}) : xs) (x,y) | t1 == x && t2 == y = False
                                                                   | otherwise = validaTorreInimigoAux xs (x,y)
-- | Verifica que a velocidade é posotiva para todos os inimigos
validaInimigosVelocidade :: [Inimigo] -> Bool
validaInimigosVelocidade [] = True
validaInimigosVelocidade ((Inimigo{velocidadeInimigo = x}) : xs) | x < 0 = False
                                                                 | otherwise = validaInimigosVelocidade xs

-- | Verifica que não existem dois ou mais projéteis do mesmo tipo em cada inimigo e verifica se não existe nenhuma combinação de Fogo e Resina ou Fogo e Gelo
validaProjeteisInimigos :: [Inimigo] -> Bool
validaProjeteisInimigos [] = True
validaProjeteisInimigos (Inimigo{projeteisInimigo = p} : xs) | validaProjeteisInimigosAux p = validaProjeteisInimigos xs
                                                             | otherwise = False
-- | Função auxiliar da função validaProjeteisInimigos que verifica para cada inimigo se as condições anteriormente descritas estão reunidas
validaProjeteisInimigosAux :: [Projetil] -> Bool
validaProjeteisInimigosAux [] = True
validaProjeteisInimigosAux p@(p1:ps) | elem x xs = False
                                     | x == Fogo && elem Resina xs = False
                                     | x == Fogo && elem Gelo xs = False 
                                     | otherwise = validaProjeteisInimigosAux ps
        where (x:xs) = projeteisInimigoConvertido p

-- | Converte uma lista de projéteis em uma lista com apenas o tipo dos projéteis, esta função ajuda na função validaProjeteisInimigosAux
projeteisInimigoConvertido :: [Projetil] -> [TipoProjetil]
projeteisInimigoConvertido [] = []
projeteisInimigoConvertido (Projetil{tipoProjetil = x} : xs) = x : projeteisInimigoConvertido xs

-- | Verifica que todas as torres estão colocadas sobre relva
validaTorreRelva :: [Torre] -> Mapa -> Bool
validaTorreRelva [] _ = True
validaTorreRelva (Torre{posicaoTorre = (x,y)} : xs) m | (mapa !! floor y !! floor x) == Terra = False
                                                      | otherwise = validaTorreRelva xs m

-- | Verifica que o alcance das torres é um valor positivo
validaAlcanceTorre :: [Torre] -> Bool
validaAlcanceTorre [] = True
validaAlcanceTorre (Torre{alcanceTorre = x} : xs) | x < 0 = False
                                                  | otherwise = validaAlcanceTorre xs                                                      

-- | Verifica que a rajada das torres é um valor positivo
validaRajadaTorre :: [Torre] -> Bool
validaRajadaTorre [] = True
validaRajadaTorre (Torre{rajadaTorre = x} : xs) | x < 0 = False
                                                | otherwise = validaRajadaTorre xs    

-- | Verifica que o ciclo das torres é um valor maior que 0
validaCicloTorre :: [Torre] -> Bool
validaCicloTorre [] = True
validaCicloTorre (Torre{cicloTorre = x} : xs) | x <= 0 = False
                                              | otherwise = validaCicloTorre xs
-- | Verifica que nenhuma torre se sobrepõe a outra
validaTorresSobrepostas :: [Torre] -> Bool
validaTorresSobrepostas l = validaTorresSobrepostasAc (validaTorresSobrepostasAux l) []
    where 
        validaTorresSobrepostasAc :: [Posicao] -> [Posicao] -> Bool
        validaTorresSobrepostasAc [] _ = True
        validaTorresSobrepostasAc (x:xs) l | elem x l = False
                                           | otherwise = validaTorresSobrepostasAc xs (x : l)

-- | Função auxiliar da função validaTorresSobrepostas que converte uma lista de torres numa lisa das suas respetivas posições
validaTorresSobrepostasAux :: [Torre] -> [Posicao]
validaTorresSobrepostasAux [] = []
validaTorresSobrepostasAux (Torre{posicaoTorre = p} : xs) = p : validaTorresSobrepostasAux xs

-- | Verifica que a base se encontra sobre terra
validaBaseTerra :: Posicao -> Mapa -> Bool
validaBaseTerra (x,y) m | (mapa !! floor y !! floor x) == Terra = True
                        | otherwise = False

-- | Verifica que os creditos da base são um valor positivo
validaCreditoBase :: Creditos -> Bool
validaCreditoBase x | x < 0 = False
                    | otherwise = True

mapa :: Mapa
mapa =
    [ [t, t, r, a, a, a],
      [r, t, r, a, r, r],
      [r, t, r, a, r, t],
      [r, t, r, a, r, t],
      [r, t, t, t, t, t],
      [a, a, a, a, r, r]
    ]
 where
    t = Terra
    r = Relva
    a = Agua
{-
jogo :: Jogo
jogo = Jogo {
    baseJogo = Base {
        vidaBase = 150,
        posicaoBase = (5.0, 2.0),
        creditosBase = 100
    },
    portaisJogo = [
        Portal {
            posicaoPortal = (0.0, 0.0),
            ondasPortal = [
                Onda {
                    inimigosOnda = [
                        Inimigo {
                            posicaoInimigo = (4.0, 3.0),
                            direcaoInimigo = Norte,
                            vidaInimigo = 80,
                            velocidadeInimigo = 1.2,
                            ataqueInimigo = 8,
                            butimInimigo = 15,
                            projeteisInimigo = []
                        },
                        Inimigo {
                            posicaoInimigo = (5.0, 3.0),
                            direcaoInimigo = Sul,
                            vidaInimigo = 100,
                            velocidadeInimigo = 1.5,
                            ataqueInimigo = 10,
                            butimInimigo = 20,
                            projeteisInimigo = []
                        }
                    ],
                    cicloOnda = 3.0,
                    tempoOnda = 3.0,
                    entradaOnda = 10.0
                }
            ]
        }
    ],
    torresJogo = [
        Torre {
            posicaoTorre = (4.0, 3.0),
            danoTorre = 30,
            alcanceTorre = 5.0,
            rajadaTorre = 2,
            cicloTorre = 1.0,
            tempoTorre = 1.0,
            projetilTorre = Projetil {
                tipoProjetil = Gelo,
                duracaoProjetil = Finita 4.0
            }
        },
        Torre {
            posicaoTorre = (2.0, 3.0),
            danoTorre = 60,
            alcanceTorre = 8.0,
            rajadaTorre = 3,
            cicloTorre = 1.2,
            tempoTorre = 0.5,
            projetilTorre = Projetil {
                tipoProjetil = Fogo,
                duracaoProjetil = Finita 6.0
            }
        }
    ],
    mapaJogo = mapa,
    inimigosJogo = [
        Inimigo {
            posicaoInimigo = (1.0, 1.0),
            direcaoInimigo = Sul,
            vidaInimigo = 140,
            velocidadeInimigo = 2.0,
            ataqueInimigo = 20,
            butimInimigo = 25,
            projeteisInimigo = []
        },
        Inimigo {
            posicaoInimigo = (1.0, 2.0),
            direcaoInimigo = Este,
            vidaInimigo = 120,
            velocidadeInimigo = 1.8,
            ataqueInimigo = 18,
            butimInimigo = 22,
            projeteisInimigo = []
        }
    ],
    lojaJogo = [
        (80, Torre {
            posicaoTorre = (0.0, 0.0),
            danoTorre = 40,
            alcanceTorre = 4.0,
            rajadaTorre = 2,
            cicloTorre = 1.5,
            tempoTorre = 0.5,
            projetilTorre = Projetil {
                tipoProjetil = Fogo,
                duracaoProjetil = Finita 3.0
            }
        }),
        (120, Torre {
            posicaoTorre = (9.0, 9.0),
            danoTorre = 80,
            alcanceTorre = 10.0,
            rajadaTorre = 1,
            cicloTorre = 2.0,
            tempoTorre = 1.0,
            projetilTorre = Projetil {
                tipoProjetil = Resina,
                duracaoProjetil = Finita 4.0
            }
        })
    ]
}



portais1 = 
   [Portal {
            posicaoPortal = (0.0, 0.0),
            ondasPortal = [
                Onda {
                    inimigosOnda = [
                        Inimigo {
                            posicaoInimigo = (4.0, 3.0),
                            direcaoInimigo = Norte,
                            vidaInimigo = 80,
                            velocidadeInimigo = 1.2,
                            ataqueInimigo = 8,
                            butimInimigo = 15,
                            projeteisInimigo = []
                        },
                        Inimigo {
                            posicaoInimigo = (5.0, 3.0),
                            direcaoInimigo = Sul,
                            vidaInimigo = 100,
                            velocidadeInimigo = 1.5,
                            ataqueInimigo = 10,
                            butimInimigo = 20,
                            projeteisInimigo = []
                        }
                    ],
                    cicloOnda = 3.0,
                    tempoOnda = 3.0,
                    entradaOnda = 10.0
                },
                Onda {
                    inimigosOnda = [
                        Inimigo {
                            posicaoInimigo = (4.0, 3.0),
                            direcaoInimigo = Norte,
                            vidaInimigo = 80,
                            velocidadeInimigo = 1.2,
                            ataqueInimigo = 8,
                            butimInimigo = 15,
                            projeteisInimigo = []
                        },
                        Inimigo {
                            posicaoInimigo = (5.0, 3.0),
                            direcaoInimigo = Sul,
                            vidaInimigo = 100,
                            velocidadeInimigo = 1.5,
                            ataqueInimigo = 10,
                            butimInimigo = 20,
                            projeteisInimigo = []
                        }
                    ],
                    cicloOnda = 3.0,
                    tempoOnda = 3.0,
                    entradaOnda = 0.0
                }
            ]
        }
   ]

torres1 = [
        Torre {
            posicaoTorre = (4.0, 3.0),
            danoTorre = 30,
            alcanceTorre = 5.0,
            rajadaTorre = 2,
            cicloTorre = 1.0,
            tempoTorre = 1.0,
            projetilTorre = Projetil {
                tipoProjetil = Gelo,
                duracaoProjetil = Finita 4.0
            }
        },
        Torre {
            posicaoTorre = (2.0, 3.0),
            danoTorre = 60,
            alcanceTorre = 8.0,
            rajadaTorre = 3,
            cicloTorre = 1.2,
            tempoTorre = 0.5,
            projetilTorre = Projetil {
                tipoProjetil = Fogo,
                duracaoProjetil = Finita 6.0
            }
        }
    ]

inimigos1 = [
        Inimigo {
            posicaoInimigo = (1.0, 1.0),
            direcaoInimigo = Sul,
            vidaInimigo = 140,
            velocidadeInimigo = 2.0,
            ataqueInimigo = 20,
            butimInimigo = 25,
            projeteisInimigo = [
                Projetil {
	                tipoProjetil = Gelo,
	                duracaoProjetil = Infinita
                },
                Projetil {
	                tipoProjetil = Resina,
	                duracaoProjetil = Infinita
                }
            ]
        },
        Inimigo {
            posicaoInimigo = (2.0, 2.0),
            direcaoInimigo = Este,
            vidaInimigo = 120,
            velocidadeInimigo = 1.8,
            ataqueInimigo = 18,
            butimInimigo = 22,
            projeteisInimigo = []
        }
    ]

base1 = Base {
            vidaBase = 150,
            posicaoBase = (5.0, 2.0),
            creditosBase = 100
    }

portal1 =
    Portal {
                posicaoPortal = (0.0, 0.0),
                ondasPortal = [
                    Onda {
                        inimigosOnda = [
                            Inimigo {
                                posicaoInimigo = (4.0, 3.0),
                                direcaoInimigo = Norte,
                                vidaInimigo = 80,
                                velocidadeInimigo = 1.2,
                                ataqueInimigo = 8,
                                butimInimigo = 15,
                                projeteisInimigo = []
                            }
                        ]
                    }
                ]
    }
                            

torre1 = 
    Torre {
            posicaoTorre = (4.0, 3.0),
            danoTorre = 30,
            alcanceTorre = 5.0,
            rajadaTorre = 1,
            cicloTorre = 1.0,
            tempoTorre = 1.0,
            projetilTorre = Projetil {
                tipoProjetil = Gelo,
                duracaoProjetil = Finita 4.0
            }
    }
    -}
Leave a Comment