Juegos de Azar y Programación Declarativa
description
Transcript of Juegos de Azar y Programación Declarativa
![Page 1: Juegos de Azar y Programación Declarativa](https://reader035.fdocument.pub/reader035/viewer/2022062305/56815008550346895dbddfbf/html5/thumbnails/1.jpg)
Juegos de Azary
Programación Declarativa
Alejandro Pérez RocaDaniel Martín Prieto
Jorge Tudela Glez de Riancho
![Page 2: Juegos de Azar y Programación Declarativa](https://reader035.fdocument.pub/reader035/viewer/2022062305/56815008550346895dbddfbf/html5/thumbnails/2.jpg)
Índice
• Introducción.• Reglas de los juegos.• Recopilación y formateo de datos.• Programas de obtención de premios.
o Apuestas múltiples.o Diferencias entre los tipos de juegos.
• Programas de búsqueda de patrones.o Patrones numéricos.o Patrones visuales.
• Resultados curiosos.• Ventajas de utilizar un lenguaje declarativo.
![Page 3: Juegos de Azar y Programación Declarativa](https://reader035.fdocument.pub/reader035/viewer/2022062305/56815008550346895dbddfbf/html5/thumbnails/3.jpg)
Introducción
![Page 4: Juegos de Azar y Programación Declarativa](https://reader035.fdocument.pub/reader035/viewer/2022062305/56815008550346895dbddfbf/html5/thumbnails/4.jpg)
Introducción
• Nuestro objetivo consistía en conseguir implementar los juegos del Euromillón, la Primitiva y la Quiniela basándonos en la historia de dichos juegos, aprovechando las ventajas tanto de eficiencia como de elegancia que nos proporciona un lenguaje declarativo.
• Además, pretendíamos descubrir y analizar resultados curiosos aplicando patrones para conocer hasta qué punto es viable el uso de estos a la hora de apostar.
![Page 5: Juegos de Azar y Programación Declarativa](https://reader035.fdocument.pub/reader035/viewer/2022062305/56815008550346895dbddfbf/html5/thumbnails/5.jpg)
Reglas de los juegos
![Page 6: Juegos de Azar y Programación Declarativa](https://reader035.fdocument.pub/reader035/viewer/2022062305/56815008550346895dbddfbf/html5/thumbnails/6.jpg)
Reglas del Euromillón
• Juego de lotería europeo organizado por diferentes instituciones de Loterías, entre ellas Loterías y Apuestas del Estado.
• El sorteo tiene lugar los viernes y salvo excepción se juega a las 21.30 en París.
• El juego consiste en elegir 5 números entre 1 y 50 y otros dos números comprendidos entre el 1 y el 9, conocidos como estrellas.
• Las apuestas múltiples consisten en marcar más de 5 números hasta un máximo de 10.
• No se pueden marcar más de dos estrellas.
![Page 7: Juegos de Azar y Programación Declarativa](https://reader035.fdocument.pub/reader035/viewer/2022062305/56815008550346895dbddfbf/html5/thumbnails/7.jpg)
Reglas del Euromillón
El precio de cada apuesta es de 2 €, destinándose el 50% de la recaudación íntegra a los premios de la siguiente forma:• 16.00% para premios de la Primera Categoría (5
números y dos estrellas).• 3.70% para premios de la Segunda Categoría (5
números y una estrella).• 1.05% para premios de la Tercera Categoría (5
números y ninguna estrella).• 0.75% para premios de la Cuarta Categoría (4
números y dos estrellas).• 0.50% para premios de la Quinta Categoría (4
números y una estrella).• ...
![Page 8: Juegos de Azar y Programación Declarativa](https://reader035.fdocument.pub/reader035/viewer/2022062305/56815008550346895dbddfbf/html5/thumbnails/8.jpg)
Reglas de la Primitiva
• Es un juego de azar cuyo primer sorteo data de 1985, se celebran unos 100 sorteos al año y, hasta hoy, más de 2300.
• Consiste en elegir seis números diferentes entre el 1 y el 49.
• Se pueden realizar apuestas múltiples eligiendo 5,7,8,9,10 u 11 números.
• El sorteo se realiza extrayendo siete bolas al azar de un bombo de 49 bolas numeradas del 1 al 49.o Las seis primeras son la combinación ganadora.o La séptima es el complementario.
• Por último se extrae el reintegro de un bombo de diez bolas numeradas del 0 al 9.
![Page 9: Juegos de Azar y Programación Declarativa](https://reader035.fdocument.pub/reader035/viewer/2022062305/56815008550346895dbddfbf/html5/thumbnails/9.jpg)
Reglas de la Primitiva
El precio de cada apuesta es de 1 €, destinándose el 55% de la recaudación íntegra a los premios de la siguiente forma:• 52% para premios de la Primera Categoría (6 aciertos).• 8% para premios de la Segunda Categoría (5 aciertos
+ complementario).• 16% para premios de la Tercera Categoría (5 aciertos).• 24% para premios de la Cuarta Categoría (4 aciertos).• 8 € para premios de la Quinta Categoría (3 aciertos).
• El reintegro es asignado aleatoriamente por el sistema a cada boleto.
![Page 10: Juegos de Azar y Programación Declarativa](https://reader035.fdocument.pub/reader035/viewer/2022062305/56815008550346895dbddfbf/html5/thumbnails/10.jpg)
Reglas de la Quiniela
• Se basa en los resultados de la LFP.• El juego consiste en el sistema 1X2, donde sólo se
pronostica si gana el equipo local (1), visitante (2), o si el partido queda empate (X).
• La apuesta se realiza sobre una lista de 15 partidos, donde 'El Pleno al 15' sólo se tiene en cuenta en caso de haber acertado los 14 restantes.
• Además de la apuesta sencilla, hay otras formas de apostar, pudiendo seleccionar para un mismo partido dos resultados posibles (doble) o incluso los tres (triple).
![Page 11: Juegos de Azar y Programación Declarativa](https://reader035.fdocument.pub/reader035/viewer/2022062305/56815008550346895dbddfbf/html5/thumbnails/11.jpg)
Reglas de la Quiniela
El precio de cada apuesta es de 0.50 €, destinándose el 55% de la recaudación íntegra a los premios de la siguiente forma:• 10% para premios de la Categoría Especial (Primera
Categoría + Pleno al 15).• 12% para premios de la Primera Categoría (14 aciertos)*.• 8% para premios de la Segunda Categoría (13 aciertos)*.• 8% para premios de la Tercera Categoría (12 aciertos)*.• 8% para premios de la Cuarta Categoría (11 aciertos)*.• 9% para premios de la Quinta Categoría (10 aciertos)*.
(*) Sin contar el Pleno al 15.
![Page 12: Juegos de Azar y Programación Declarativa](https://reader035.fdocument.pub/reader035/viewer/2022062305/56815008550346895dbddfbf/html5/thumbnails/12.jpg)
Recopilacióny
formateo de datos
![Page 13: Juegos de Azar y Programación Declarativa](https://reader035.fdocument.pub/reader035/viewer/2022062305/56815008550346895dbddfbf/html5/thumbnails/13.jpg)
Datos originales
• Los datos de historia de los tres juegos han sido recopilados de www.onlae.es, la web oficial de Loterías y Apuestas del Estado.
• Dichos datos, han sido copiados tal cual a un fichero de texto plano.
![Page 14: Juegos de Azar y Programación Declarativa](https://reader035.fdocument.pub/reader035/viewer/2022062305/56815008550346895dbddfbf/html5/thumbnails/14.jpg)
Datos originales
• De esta forma quedaría el fichero original, el cual tenemos que formatear porque hay información que no nos interesa:
![Page 15: Juegos de Azar y Programación Declarativa](https://reader035.fdocument.pub/reader035/viewer/2022062305/56815008550346895dbddfbf/html5/thumbnails/15.jpg)
Datos formateados
• Al fichero anterior, se le ha aplicado un filtro Lex para eliminar información que no queremos y adaptar el formato a las listas de Haskell, a través de las que se accederán a los datos para que los programas realicen los cálculos.o La primera lista es la apuesta ganadora.o La segunda la forman ventas, bote y acertantes con sus
premios correspondientes.
![Page 16: Juegos de Azar y Programación Declarativa](https://reader035.fdocument.pub/reader035/viewer/2022062305/56815008550346895dbddfbf/html5/thumbnails/16.jpg)
Datos formateados
• Mediante la función readFile iremos accediendo a la información de forma perezosa, según lo vayan necesitando las funciones correspondientes, de una forma simple y cómoda.
quiniela :: String -> IO ()quiniela apuesta = do x <- readFile historiaQuinielalet ganancias = calcularPremioHistoria ((traducir . words) apuesta) (lines x) ... ...
![Page 17: Juegos de Azar y Programación Declarativa](https://reader035.fdocument.pub/reader035/viewer/2022062305/56815008550346895dbddfbf/html5/thumbnails/17.jpg)
Datos formateados
• Además, debido a que Haskell es un lenguaje de programación fuertemente tipado y para que haya coherencia de tipos, utilizamos la función read, que convierte un String al tipo inferido.
calcularPremioHistoria :: [Int] -> [String] -> FloatcalcularPremioHistoria _ [] = 0.0calcularPremioHistoria apuesta (ganadora:premios:restoHistoria) = calcularPremioDía apuesta (read ganadora) (read premios) + calcularPremioHistoria apuesta restoHistoria
![Page 18: Juegos de Azar y Programación Declarativa](https://reader035.fdocument.pub/reader035/viewer/2022062305/56815008550346895dbddfbf/html5/thumbnails/18.jpg)
Programas de obtenciónde premios
![Page 19: Juegos de Azar y Programación Declarativa](https://reader035.fdocument.pub/reader035/viewer/2022062305/56815008550346895dbddfbf/html5/thumbnails/19.jpg)
Programas de obtención de premios• En esta sección se van a mostrar las funciones
necesarias para implementar un programa que:
o Reciba como entrada una combinación.o Nos devuelva las ganancias y pérdidas que
obtendríamos si hubiésemos jugado con esta combinación durante toda la historia de la lotería.
![Page 20: Juegos de Azar y Programación Declarativa](https://reader035.fdocument.pub/reader035/viewer/2022062305/56815008550346895dbddfbf/html5/thumbnails/20.jpg)
Código del Euromillón
• Lectura de ficheros y llamada al programa de cómputo.
euromillón :: [Int] -> IO ()euromillón apuesta = do x <- readFile historiaEuromillónlet ganancias = calcularPremioHistoria apuesta (lines x)let pérdidas = fromIntegral ((length . lines) x)/2 * calcularPrecioApuesta apuestaprint (ganancias, pérdidas, ganancias - pérdidas)
![Page 21: Juegos de Azar y Programación Declarativa](https://reader035.fdocument.pub/reader035/viewer/2022062305/56815008550346895dbddfbf/html5/thumbnails/21.jpg)
Código del Euromillón
• Cálculo del precio de una apuesta. Apuestas múltiples.
calcularPrecioApuesta :: [Int] -> FloatcalcularPrecioApuesta apuesta =2.0 * fromIntegral (comb ((length apuesta)-2) 5)
• Cálculo de las ganancias generadas por una apuesta durante toda la historia.
calcularPremioHistoria :: [Int] -> [String] -> FloatcalcularPremioHistoria _ [] = 0.0calcularPremioHistoria apuesta (ganadora:premios:restoHistoria) = calcularPremioDía apuesta (read ganadora) (read premios)+ calcularPremioHistoria apuesta restoHistoria
![Page 22: Juegos de Azar y Programación Declarativa](https://reader035.fdocument.pub/reader035/viewer/2022062305/56815008550346895dbddfbf/html5/thumbnails/22.jpg)
Código del Euromillón
• Cálculo de las ganancias generadas por una apuesta en un día.
calcularPremioDía :: [Int] -> [Int] -> [Float] -> FloatcalcularPremioDía apuesta ganadora premios = calcularPremioCategoría (categoríaPremio apuesta ganadora) premios
• Obtención de la categoría del acierto.categoríaPremio :: [Int] -> [Int] -> IntcategoríaPremio apuesta ganadora = calcularCategoría (númeroAciertos ((init . init) apuesta) (take 5 ganadora))(númeroAciertos (drop ((length apuesta)-2) apuesta) (drop 5 ganadora))
![Page 23: Juegos de Azar y Programación Declarativa](https://reader035.fdocument.pub/reader035/viewer/2022062305/56815008550346895dbddfbf/html5/thumbnails/23.jpg)
Código del Euromillón• Obtención del número de aciertos.
númeroAciertos :: [Int] -> [Int] -> IntnúmeroAciertos [] _ = 0númeroAciertos (x:xs) ys| elem x ys = 1 + númeroAciertos xs ys| otherwise = númeroAciertos xs ys
• Cálculo de la categoría en función del número de aciertos.
calcularCategoría :: Int -> Int -> IntcalcularCategoría números estrellas = case (números,estrellas) of(5,_) -> 3 - estrellas(4,_) -> 6 - estrellas(3,0) -> 10(3,_) -> 9 - estrellas(2,2) -> 9(2,1) -> 12(1,2) -> 11(_,_) -> 0
![Page 24: Juegos de Azar y Programación Declarativa](https://reader035.fdocument.pub/reader035/viewer/2022062305/56815008550346895dbddfbf/html5/thumbnails/24.jpg)
Código del Euromillón• Obtención del premio obtenido según la categoría.calcularPremioCategoría :: Int -> [Float] -> FloatcalcularPremioCategoría categoría (ventas:bote:premios) = case categoría of0 -> 0.01 -> bote/(premios!!0 + 1)2 -> (ventas/2)*0.074/(premios!!2 + 1)3 -> (ventas/2)*0.021/(premios!!4 + 1)4 -> (ventas/2)*0.015/(premios!!6 + 1)5 -> (ventas/2)*0.010/(premios!!8 + 1)6 -> (ventas/2)*0.007/(premios!!10 + 1)7 -> (ventas/2)*0.010/(premios!!12 + 1)8 -> (ventas/2)*0.051/(premios!!14 + 1)9 -> (ventas/2)*0.044/(premios!!16 + 1)10 -> (ventas/2)*0.047/(premios!!18 + 1)11 -> (ventas/2)*0.101/(premios!!20 + 1)12 -> (ventas/2)*0.240/(premios!!22 + 1)
![Page 25: Juegos de Azar y Programación Declarativa](https://reader035.fdocument.pub/reader035/viewer/2022062305/56815008550346895dbddfbf/html5/thumbnails/25.jpg)
Código de la Primitiva
• En la primitiva hay que tener en cuenta el reintegro. calcularPremioDia :: [Int] -> [Int] -> [Float] -> FloatcalcularPremioDia apuesta ganadora premios = calcularPremioCategoria (categoriaPremio apuesta ganadora) premios + reintegro where reintegro = if last apuesta == last ganadora then calcularPrecioApuesta apuesta else 0
![Page 26: Juegos de Azar y Programación Declarativa](https://reader035.fdocument.pub/reader035/viewer/2022062305/56815008550346895dbddfbf/html5/thumbnails/26.jpg)
Código de la Primitiva
• Hay seis números en la combinación, no hay estrellas y hay que tener en cuenta el número complementario.
categoriaPremio :: [Int] -> [Int] -> IntcategoriaPremio apuesta ganadora = calcularCategoria (numeroAciertos (init apuesta)(take 6 ganadora)) (numeroAciertos (init apuesta) comple) where comple = take 1 (drop 6 ganadora)
![Page 27: Juegos de Azar y Programación Declarativa](https://reader035.fdocument.pub/reader035/viewer/2022062305/56815008550346895dbddfbf/html5/thumbnails/27.jpg)
Código de la Primitiva
• Hay cinco categorías en vez de doce, la categoría depende de la combinación ganadora y del complementario.
calcularCategoria :: Int -> Int -> IntcalcularCategoria apuesta complementario = case (apuesta,complementario)of (6,_) -> 1 (5,1) -> 2 (5,_) -> 3 (4,_) -> 4 (3,_) -> 5 (_,_) -> 0
![Page 28: Juegos de Azar y Programación Declarativa](https://reader035.fdocument.pub/reader035/viewer/2022062305/56815008550346895dbddfbf/html5/thumbnails/28.jpg)
Código de la Quiniela
• En nuestro programa de la Quiniela, la entrada es un String.o Por ejemplo: "1 1x 2 1 1 x 1x2 2 1 1 1 x 2 2 1x".
• Para manejar la apuesta mejor, hacemos una traducción a lista de enteros.
traducir :: [String] -> [Int]traducir [] = []traducir (x:xs)| x=="1" = (1:traducir xs)| x=="2" = (2:traducir xs)| x=="x" = (3:traducir xs)| x=="12" = (4:traducir xs)| x=="1x" = (5:traducir xs)| x=="x2" = (6:traducir xs)| x=="1x2" = (7:traducir xs)| x=="?" = (8:traducir xs) -- Esto es para los patrones.
![Page 29: Juegos de Azar y Programación Declarativa](https://reader035.fdocument.pub/reader035/viewer/2022062305/56815008550346895dbddfbf/html5/thumbnails/29.jpg)
Código de la Quiniela
• Para calcular el precio hay que saber cuántas apuestas se han hecho, en función de los dobles y triples que contenga.
númeroApuestas :: [Int] -> IntnúmeroApuestas [] = 1númeroApuestas (x:xs)| esDoble x = 2 * númeroApuestas xs| esTriple x = 3 * númeroApuestas xs| otherwise = númeroApuestas xs
• Los dobles son 4, 5 y 6 (12, 1x, x2).• El triple es 7 (1x2).
![Page 30: Juegos de Azar y Programación Declarativa](https://reader035.fdocument.pub/reader035/viewer/2022062305/56815008550346895dbddfbf/html5/thumbnails/30.jpg)
Código de la Quiniela
• También es diferente con respecto al Euromillón y la Primitiva el hecho de que el orden a la hora de hacer la apuesta importa.o No es lo mismo "1 2 x..." que "1 x 2...".
númeroAciertos :: [Int] -> [Int] -> IntnúmeroAciertos apuesta ganadora = sum (zipWith (\x y -> if (acierta x y) then 1 else 0) apuesta ganadora)
• La función acierta tiene en cuenta los dobles y triples.o Si apuesta = "x2 1..." y ganadora = "x x..."
devolvería un acierto en el primer elemento de las combinaciones.
![Page 31: Juegos de Azar y Programación Declarativa](https://reader035.fdocument.pub/reader035/viewer/2022062305/56815008550346895dbddfbf/html5/thumbnails/31.jpg)
Código de la Quiniela
• El número de aciertos nos sirve para conocer la categoría del premio al que hubiéramos optado.
calcularCategoría :: Int -> Int -> IntcalcularCategoría aciertos aciertoPleno = case (aciertos,aciertoPleno) of (14,1) -> 6 -- Pleno al 15. (14,0) -> 1 -- Categoría 1. (13,_) -> 2 -- Categoría 2. (12,_) -> 3 (11,_) -> 4 (10,_) -> 5 (_,_) -> 0 -- Categoría 0 == No premiado.
![Page 32: Juegos de Azar y Programación Declarativa](https://reader035.fdocument.pub/reader035/viewer/2022062305/56815008550346895dbddfbf/html5/thumbnails/32.jpg)
Programas de búsquedade patrones
![Page 33: Juegos de Azar y Programación Declarativa](https://reader035.fdocument.pub/reader035/viewer/2022062305/56815008550346895dbddfbf/html5/thumbnails/33.jpg)
Búsqueda de Patrones
• ¿Qué es un patrón?
o Definición de la RAE: Modelo que sirve de muestra para sacar otra cosa igual.
o Nuestra definición: Conjunto de apuestas que
tienen algo en común.
![Page 34: Juegos de Azar y Programación Declarativa](https://reader035.fdocument.pub/reader035/viewer/2022062305/56815008550346895dbddfbf/html5/thumbnails/34.jpg)
Búsqueda de Patrones
• Diferenciamos dos tipos de patrones:
o Patron numérico: Conjunto de apuestas cuyos números guardan algún tipo de relación matemática.
o Ejemplos: Apuesta con números primos. Apuesta con números menores que diez. Apuesta de números pares.
o Patrón visual: Conjunto de apuestas que al ser marcadas sobre el boleto, representan una figura.
![Page 35: Juegos de Azar y Programación Declarativa](https://reader035.fdocument.pub/reader035/viewer/2022062305/56815008550346895dbddfbf/html5/thumbnails/35.jpg)
Búsqueda de Patrones
• Ejemplo: patrones visuales en el boleto de la Quiniela.
![Page 36: Juegos de Azar y Programación Declarativa](https://reader035.fdocument.pub/reader035/viewer/2022062305/56815008550346895dbddfbf/html5/thumbnails/36.jpg)
Búsqueda de Patrones
• Nuestro objetivo es conocer si la gente juega de forma aleatoria o sigue algún patrón.
• ¿Cómo podemos saber a qué juega la gente si el número de apuestas no es público?
• Sólo tenemos información sobre las ventas de cada sorteo y el número de acertantes de cada categoría.
Ratio ventas/acertantes:o La ratio de las ventas entre la suma de los
acertantes de N categorías, es un indicador de la cantidad de gente que ha ganado apostando con un determinado patrón.
![Page 37: Juegos de Azar y Programación Declarativa](https://reader035.fdocument.pub/reader035/viewer/2022062305/56815008550346895dbddfbf/html5/thumbnails/37.jpg)
Búsqueda de Patrones
• Necesitamos poder comparar la ratio de cada combinación con alguna referencia.
• Nuestra referencia será la media de esa ratio en todos los sorteos de los que tenemos información.
imprimirMedia :: [String] -> FloatimprimirMedia [] = 0.0imprimirMedia (combinacion:premios:historia) = (head . calcularRatio . read) premios + imprimirMedia historia
![Page 38: Juegos de Azar y Programación Declarativa](https://reader035.fdocument.pub/reader035/viewer/2022062305/56815008550346895dbddfbf/html5/thumbnails/38.jpg)
Búsqueda de Patrones
• Esto nos permite conocer cuánto ha sido apostada una combinación o un conjunto de combinaciones (patrón).
• Cuanto mayor sea nuestro indicador para una combinación, menos acertantes se habrán producido y viceversa.• Los programas que hemos implementado utilizan este
indicador de forma que, dado un patrón y un predicado, nos devuelven el conjunto de combinaciones que lo cumplen, las ventas, los acertantes y la ratio ventas entre acertantes, para cada combinación seleccionada.
• El predicado servirá para filtrar las combinaciones en función del número de coincidencias.
![Page 39: Juegos de Azar y Programación Declarativa](https://reader035.fdocument.pub/reader035/viewer/2022062305/56815008550346895dbddfbf/html5/thumbnails/39.jpg)
Patrones numéricos: Euromillón
• Lectura de ficheros y llamada al programa de cómputo.
• patrón :: (Int -> Bool) -> (Float -> Bool) -> Int -> IO ()• patrón f g n = do • x <- readFile historiaEuromillón• imprimirPatron (lines x) f g n
• Selección de combinaciones a imprimir.
imprimirPatron :: [String] -> (Int -> Bool) -> (Float -> Bool) -> Int -> IO ()imprimirPatron (combinacion:premios:historia) f g n| (length (filter f (take 5 (read combinacion))) >= n) && ((g . head . calcularRatio . read) premios) = doimprimirPatron historia f g nprint (combinacion ++ " --> " ++ show((calcularRatio . read) premios)))| otherwise = imprimirPatron historia f g nimprimirPatron [] _ _ _ = putStr ""
![Page 40: Juegos de Azar y Programación Declarativa](https://reader035.fdocument.pub/reader035/viewer/2022062305/56815008550346895dbddfbf/html5/thumbnails/40.jpg)
Patrones numéricos: Euromillón
• Calculamos la ratio de la siguiente forma: Las ventas que se produjeron para ese sorteo entre el número de acertantes totales.
calcularRatio :: [Float] -> [Float]calcularRatio (ventas:bote:acertantes) = [ventas/(sumarAcertantes acertantes 8),ventas,sumarAcertantes acertantes 8]
• Para que el valor obtenido sea más representativo no hemos tenido en cuenta los acertantes de las últimas categorías ya que siempre hay muchos y no nos dan una información significativa.
![Page 41: Juegos de Azar y Programación Declarativa](https://reader035.fdocument.pub/reader035/viewer/2022062305/56815008550346895dbddfbf/html5/thumbnails/41.jpg)
Patrones visuales: Primitiva
• Todas las apuestas de un patrón cumplen que:
o La distancia de todos los números respecto al
primero siempre es la misma.
o La distancia en columnas de todos los números respecto al primero siempre es la misma.
![Page 42: Juegos de Azar y Programación Declarativa](https://reader035.fdocument.pub/reader035/viewer/2022062305/56815008550346895dbddfbf/html5/thumbnails/42.jpg)
Patrones visuales: Primitiva
• Ejemplo: Las dos apuestas pertenecen al mismo patrón. o [1,11,12,21,22,32] == [17,27,28,37,38,48]
![Page 43: Juegos de Azar y Programación Declarativa](https://reader035.fdocument.pub/reader035/viewer/2022062305/56815008550346895dbddfbf/html5/thumbnails/43.jpg)
Patrones visuales: Primitiva
• La función patron recibe como parámetro la primera combinación que pertenece a un patrón y un predicado p que sirve para filtrar las combinaciones en función del número de coincidencias con el patrón.
• Muestra la combinación, la ratio, las ventas, el nº de acertantes y dibuja la combinación.
patron :: [Int]-> (Int -> Bool) -> IO[()]patron pat p = do x <- readFile historiaPrimitiva dibujaBoleto pat mapM (sequence_ . dibujaResultados)(buscaPatrones (sort pat) (lines x) p)
![Page 44: Juegos de Azar y Programación Declarativa](https://reader035.fdocument.pub/reader035/viewer/2022062305/56815008550346895dbddfbf/html5/thumbnails/44.jpg)
Patrones visuales: Primitiva
• Selección de combinaciones que pertenecen al patrón.
buscaPatrones :: [Int] -> [String] -> (Int -> Bool) -> [([Int],[Float])]buscaPatrones _ [] _ = []buscaPatrones [] _ _ = []buscaPatrones patron (ganadora:premios:restoHistoria) p | encajaPatron ((take 7 . read) ganadora) (creaTodosPatrones patron) p = (((sort . take 7 . read)ganadora),calcularRatio (read premios)):buscaPatrones patron restoHistoria p |otherwise = buscaPatrones patron restoHistoria p
![Page 45: Juegos de Azar y Programación Declarativa](https://reader035.fdocument.pub/reader035/viewer/2022062305/56815008550346895dbddfbf/html5/thumbnails/45.jpg)
Patrones visuales: Primitiva
• La función creaTodosPatrones recibe la menor apuesta que es cubierta por el patrón, y devuelve lista con todas las apuestas cubiertas por el patrón.
creaTodosPatrones :: [Int] -> [[Int]]creaTodosPatrones patron = creaPatrones (sort patron) (creaDesp (sort patron))
• encajaPatron comprueba que una combinación pertenece a un patrón, satisfaciendo el predicado p.
encajaPatron :: [Int] -> [[Int]] -> (Int -> Bool) -> BoolencajaPatron apuesta patron p = p (foldr (\x y -> max (numeroAciertos x apuesta) y) 0 patron)
![Page 46: Juegos de Azar y Programación Declarativa](https://reader035.fdocument.pub/reader035/viewer/2022062305/56815008550346895dbddfbf/html5/thumbnails/46.jpg)
Patrones visuales: Primitiva
• Hemos definido una función constante que devuelve la estructura del boleto.
_boleto = " 00 10 20 30 40\n 01 11 21 31 41\n 02 12 22 32 42\n 03 13 23 33 43" ++"\n 04 14 24 34 44\n 05 15 25 35 45\n 06 16 26 36 46\n 07 17 27 37 47" ++"\n 08 18 28 38 48\n 09 19 29 39 49"
• Sobre _boleto, sustituimos los números de una combinación por la cadena "__", y los mostramos por pantalla.
![Page 47: Juegos de Azar y Programación Declarativa](https://reader035.fdocument.pub/reader035/viewer/2022062305/56815008550346895dbddfbf/html5/thumbnails/47.jpg)
Patrones visuales: Primitiva
• Funciones para crear un boleto y mostrarlo por pantalla.
dibujaBoleto :: [Int] -> IO()dibujaBoleto = do putStrLn . ("\n" ++) . creaBoletoVisual creaBoletoVisual :: [Int] -> StringcreaBoletoVisual combinacion = unlines (map unwords (sustituyeBoleto combinacion "__" (map words (lines _boleto))))
![Page 48: Juegos de Azar y Programación Declarativa](https://reader035.fdocument.pub/reader035/viewer/2022062305/56815008550346895dbddfbf/html5/thumbnails/48.jpg)
Patrones visuales: Quiniela
• Hemos implementado la función pintar, que dado un patrón o una apuesta, la muestra por pantalla como si fuera el boleto.
pintar :: String -> IO()pintar apuesta =(putStrLn . ("\n 1 X 2\n " ++) . unwords) (zipWith (++) pintaColumna (map traducirGráfico (words apuesta)))
pintaColumna :: [String]pintaColumna = map (\x -> if (x<10) then " "++show(x) else show(x)) [1..15]traducirGráfico :: String -> StringtraducirGráfico simb = case simb of "?" -> " \n" "1" -> " * \n" "x" -> " * \n" "2" -> " * \n" "1x" -> " * * \n" "12" -> " * * \n" "x2" -> " * * \n" "1x2" -> " * * * \n"
![Page 49: Juegos de Azar y Programación Declarativa](https://reader035.fdocument.pub/reader035/viewer/2022062305/56815008550346895dbddfbf/html5/thumbnails/49.jpg)
Patrones visuales: Quiniela
• La función patrón, a la que se le pasa un patrón (puede tener filas no marcadas, señaladas con '?') y un número mínimo de aciertos que debe proporcionarnos dicho patrón, nos lo muestra por pantalla y nos da una relación de combinaciones ganadoras que cumplen ese mínimo y los aciertos y la ratio anteriormente explicado.
patrón :: String -> Int -> IO()patrón p error = do x <- readFile historiaQuiniela pintar p imprimirPatrón (lines x) (traducir (words p)) error
![Page 50: Juegos de Azar y Programación Declarativa](https://reader035.fdocument.pub/reader035/viewer/2022062305/56815008550346895dbddfbf/html5/thumbnails/50.jpg)
Patrones visuales: Quiniela
imprimirPatrón :: [String] -> [Int] -> Int -> IO()imprimirPatrón (combinación:premios:historia) patrón error | (númeroAciertos patrón (read combinación)) >= error = doimprimirPatrón historia patrón errorprint ((bonita (read combinación)) ++ " --> " ++ show (númeroAciertos patrón (read combinación)) ++ " --> " ++ show (calcularRatio (read premios))) | otherwise = imprimirPatrón historia patrón errorimprimirPatrón [] _ _ = putStr ""
![Page 51: Juegos de Azar y Programación Declarativa](https://reader035.fdocument.pub/reader035/viewer/2022062305/56815008550346895dbddfbf/html5/thumbnails/51.jpg)
Resultados curiosos
![Page 52: Juegos de Azar y Programación Declarativa](https://reader035.fdocument.pub/reader035/viewer/2022062305/56815008550346895dbddfbf/html5/thumbnails/52.jpg)
Resultados curiosos: Euromillón
• La media del Euromillón es: 995.69• Nunca han salido todos los números primos.
*Main> patrón esPrimo (\x->True) 5• Combinaciones en las que todos los números han
sido primos menos 1. *Main> patrón esPrimo (\x->True) 4 "[1,3,13,16,43,2,7] --> [781.4534,4.447564e7,56914.0]" "[11,24,29,31,41,1,3] --> [940.5416,6.571188e7,69866.0]" "[1,7,17,31,36,2,6] --> [1068.585,6.2435284e7,58428.0]" "[1,11,18,19,47,3,7] --> [894.38617,8.023628e7,89711.0]" "[5,13,17,40,43,1,3] --> [911.02264,8.32784e7,91412.0]" "[9,11,13,19,37,3,4] --> [700.02014,7.2343584e7,103345.0]" "[11,13,18,37,47,1,6] --> [1120.8005,8.1279336e7,72519.0]" "[30,47,2,37,5,6,3] --> [1246.2428,8.371511e7,67174.0]" "[38,47,31,19,5,3,5] --> [994.6379,1.358188e8,136551.0]"
![Page 53: Juegos de Azar y Programación Declarativa](https://reader035.fdocument.pub/reader035/viewer/2022062305/56815008550346895dbddfbf/html5/thumbnails/53.jpg)
Resultados curiosos: Euromillón
• Combinaciones acertadas por pocos jugadores. *Main> patrón (\x->True) (>media+380) 5 "[26,36,37,38,42,2,6] --> [1388.5532,6.4070624e7,46142.0]" "[35,36,42,46,49,2,8] --> [1376.8063,9.309964e7,67620.0]" "[41,43,44,46,20,2,9] --> [1439.7476,9.216832e7,64017.0]"
• Combinaciones acertadas por muchos jugadores. *Main> patrón (\x->True) (<media-350) 5 "[10,12,14,24,26,5,8] --> [643.3405,4.1352e7,64277.0]" "[5,10,17,22,33,5,7] --> [617.76953,7.101755e7,114958.0]"
• Números pares. *Main> patrón even (<700) 5 "[10,12,14,24,26,5,8] --> [643.3405,4.1352e7,64277.0]" "[10,20,30,36,40,3,5] --> [694.91895,7.7093616e7,110939.0]"
![Page 54: Juegos de Azar y Programación Declarativa](https://reader035.fdocument.pub/reader035/viewer/2022062305/56815008550346895dbddfbf/html5/thumbnails/54.jpg)
Resultados curiosos: Primitiva
• La media de la ratio para la Primitiva es 41.15.
• El patrón escalera es bastante jugado.
00 10 __ __ 40 01 __ __ 31 41 __ __ 22 32 42 03 13 23 33 43 04 14 24 34 44 05 15 25 35 45 06 16 26 36 46 .... [18,21,22,30,31,40,41] --> [39.88779,1.4704155e7,368638.0] [9,13,18,19,27,28,33] --> [28.898842,1.5286129e7,528953.0] [7,8,17,25,26,32,35] --> [28.170479,9128165.0,324033.0]
![Page 55: Juegos de Azar y Programación Declarativa](https://reader035.fdocument.pub/reader035/viewer/2022062305/56815008550346895dbddfbf/html5/thumbnails/55.jpg)
Resultados curiosos: Primitiva
• El patrón tejado también es bastante jugado.
00 __ 20 __ 40 __ 11 21 31 __ 02 __ 22 __ 42 03 13 23 33 43 04 14 24 34 44 05 15 25 35 45 06 16 26 36 46 ....[4,8,9,30,37,39,48] --> [34.107815,1.842794e7,540285.0][2,9,17,35,37,38,46] --> [8.308153,5493658.0,661237.0][1,7,16,20,36,38,48] --> [10.617591,8330944.0,784636.0][6,15,35,38,44,46,48] --> [12.549679,7585377.0,604428.0]
![Page 56: Juegos de Azar y Programación Declarativa](https://reader035.fdocument.pub/reader035/viewer/2022062305/56815008550346895dbddfbf/html5/thumbnails/56.jpg)
Resultados curiosos: Quiniela
• La Media es 557.03.• El patrón 'zigzag':
*Main> patrón "1 x 2 x 1 x 2 x 1 x 2 x 1 x 2" 11
1 X 2 1 * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10 * 11 * 12 * 13 * 14 * 15 * "1 X 2 X 1 X 2 1 1 X 1 2 1 X X --> 11 --> 39.233353"
• Otro patrón, cubre más con 5 triples:
*Main> patrón "1 1x2 1 1 1x2 1 1 1x2 1 1 1x2 1 1 1x2 1" 14
1 X 2 1 * 2 * * * 3 * 4 * 5 * * * 6 * 7 * 8 * * * 9 * 10 * 11 * * * 12 * 13 * 14 * * * 15 *
"1 1 X 1 1 1 1 2 1 1 X 1 1 1 1 --> 14 --> 46.875664""1 1 1 1 1 X 1 1 1 1 1 1 1 2 1 --> 14 --> 13.047872"
![Page 57: Juegos de Azar y Programación Declarativa](https://reader035.fdocument.pub/reader035/viewer/2022062305/56815008550346895dbddfbf/html5/thumbnails/57.jpg)
Resultados curiosos: Quiniela
• El patrón 'todos unos':*Main> patrón "1 1 1 1 1 1 1 1 1 1 1 1 1 1 1" 12
1 X 2 1 * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10 * 11 * 12 * 13 * 14 * 15 *
• Los resultados muestran que mucha gente juega a este patrón:
"1 1 1 1 1 1 X 1 2 1 1 1 2 1 1 --> 12 --> 64.979614""1 1 X 1 1 1 1 2 1 1 X 1 1 1 1 --> 12 --> 46.875664""1 1 1 X 1 1 1 X 1 1 1 X 1 1 1 --> 12 --> 38.72563""1 1 X 1 1 1 1 1 1 1 X X 1 1 1 --> 12 --> 14.160672""2 1 1 1 X 1 1 1 1 1 1 X 1 1 1 --> 12 --> 15.485243""1 1 1 1 1 1 1 1 2 1 1 1 X 1 2 --> 12 --> 12.125318""1 1 1 1 1 X 1 1 1 1 1 1 1 2 1 --> 13 --> 13.047872""X 1 1 X 1 1 1 1 1 X 1 1 1 1 1 --> 12 --> 13.555782"
![Page 58: Juegos de Azar y Programación Declarativa](https://reader035.fdocument.pub/reader035/viewer/2022062305/56815008550346895dbddfbf/html5/thumbnails/58.jpg)
Resultados curiosos: Quiniela
• Un patrón con prioridad al empate:
*Main> patrón "x 1x x2 x x2 1x x 1x x2 x x2 1x x 1x x2" 12
1 X 2 1 * 2 * * 3 * * 4 * 5 * * 6 * * 7 * 8 * * 9 * * 10 * 11 * * 12 * * 13 * 14 * * 15 * *
• Los resultados muestran que poca gente juega a este patrón:
"X 1 X 2 X 1 X 1 X X 1 2 X X X --> 12 --> 5953.646""X 1 X X 2 1 X 1 X 1 X 1 1 1 2 --> 13 --> 703.6132""X X 1 X X 2 X X X X 1 X X 1 X --> 12 --> 1473.1707""1 1 1 X X 1 X X 1 X 2 X X X 2 --> 12 --> 1207.189""X 1 X X 2 X 2 2 1 X X 1 X 1 X --> 12 --> 4419.3354""X 1 X X 1 1 2 1 2 X X 1 2 X 2 --> 12 --> 18.934837""2 1 2 X X X X X X 1 X X X 1 1 --> 12 --> 173.81834""X X 1 X 1 X X 1 2 X 1 X X 1 X --> 12 --> 73.66204""1 1 2 2 1 1 X 1 2 X 2 1 X 1 X --> 12 --> 229.9559""1 1 2 X X X X X 1 2 X 1 X X 2 --> 12 --> 810.53253""X X X X X 1 2 1 2 2 X X X X X --> 13 --> 537.0503""1 1 2 X X 1 X 1 1 1 2 X X X 2 --> 12 --> 71.77404""1 1 X 1 X 1 X X X X X X X 1 1 --> 12 --> 342.74445""X 1 X 1 2 1 1 1 X 2 2 1 X 1 X --> 12 --> 10.901946""1 1 2 X 2 2 X X X X 2 1 2 1 2 --> 12 --> 772.0594""X X 2 X X 2 2 X X X 1 1 X 1 X --> 12 --> 1236.6622""X 1 1 2 2 X X 1 2 X X 1 X X 1 --> 12 --> 156.02274""1 X 2 X X 1 X X 2 X X 2 1 1 X --> 12 --> 538.5936""X 1 2 X 1 1 X 1 2 X X 1 2 1 X --> 13 --> 615.06494"
![Page 59: Juegos de Azar y Programación Declarativa](https://reader035.fdocument.pub/reader035/viewer/2022062305/56815008550346895dbddfbf/html5/thumbnails/59.jpg)
Ventajas de utilizar un lenguaje declarativo
![Page 60: Juegos de Azar y Programación Declarativa](https://reader035.fdocument.pub/reader035/viewer/2022062305/56815008550346895dbddfbf/html5/thumbnails/60.jpg)
Ventajas de un lenguaje declarativo• Lectura de fichero con la función readFile.
o Mientras que con un lenguaje imperativo habríamos tenido que abrir el fichero, leerlo a trozos y finalmente cerrarlo, Haskell nos ofrece esta función mediante la que, con una sola llamada tenemos el fichero entero disponible en una variable.
o En un lenguaje imperativo habríamos tenido que construir una compleja estructura de datos para ir almacenando los datos que leyéramos. Con Haskell una variable es suficiente.
![Page 61: Juegos de Azar y Programación Declarativa](https://reader035.fdocument.pub/reader035/viewer/2022062305/56815008550346895dbddfbf/html5/thumbnails/61.jpg)
Ventajas de un lenguaje declarativo• Funciones words y lines.
o Tras la lectura del fichero, el uso de estas funciones hace que podamos tokenizar fácilmente el String de entrada.
o Es cierto que en lenguajes como Java hay clases que permiten hacer este tipo de cosas, pero sería muy ineficiente tener una variable con todo el fichero en tokens.
![Page 62: Juegos de Azar y Programación Declarativa](https://reader035.fdocument.pub/reader035/viewer/2022062305/56815008550346895dbddfbf/html5/thumbnails/62.jpg)
Ventajas de un lenguaje declarativo• Función read.
o Esta función nos ofrece una manera elegante y simple de parsear el String de entrada y convertirlo al tipo inferido.
o En un lenguaje imperativo esta operación habría necesitado conversiones de String al tipo deseado utilizando funciones diferentes para cada tipo.
o Con Haskell sólo hemos usado una función para todos los tipos, y además no hemos tenido que indicar el tipo de la conversión explícitamente.
![Page 63: Juegos de Azar y Programación Declarativa](https://reader035.fdocument.pub/reader035/viewer/2022062305/56815008550346895dbddfbf/html5/thumbnails/63.jpg)
Ventajas de un lenguaje declarativo• Implementación de apuestas múltiples
o Añadir a nuestros programas la posibilidad de realizar apuestas múltiples requirió muy pocos cambios.
o Haskell ofrece funciones muy sencillas para realizar operaciones como quitar los n últimos elementos de una lista u obtener los m primeros de otra.
o En un lenguaje imperativo esto habría necesitado del uso de índices que siempre son engorrosos.
![Page 64: Juegos de Azar y Programación Declarativa](https://reader035.fdocument.pub/reader035/viewer/2022062305/56815008550346895dbddfbf/html5/thumbnails/64.jpg)
Ventajas de un lenguaje declarativo• Listas y operaciones sobre listas.
o Al ser todos los datos de entrada listas de números, el completo manejo que ofrece Haskell a la hora de manejarlas ha resultado muy útil. Pattern matching. Operaciones sobre listas. Maps y plegados.
• Funciones lambda.o Nos han permitido pasar funciones como
parámetro fácilmente.o Posibilidad de poder aplicarlas sin necesidad de
declararlas en el código hace que fácilmente podamos probar patrones numéricos.
![Page 65: Juegos de Azar y Programación Declarativa](https://reader035.fdocument.pub/reader035/viewer/2022062305/56815008550346895dbddfbf/html5/thumbnails/65.jpg)
Gracias por su atención
Alejandro Pérez RocaDaniel Martín Prieto
Jorge Tudela Glez de Riancho