1 / 65

Juegos de Azar y Programación Declarativa

Alejandro Pérez Roca Daniel Martín Prieto Jorge Tudela Glez de Riancho. Juegos de Azar y Programación Declarativa. Índice. Introducción. Reglas de los juegos. Recopilación y formateo de datos. Programas de obtención de premios. Apuestas múltiples. Diferencias entre los tipos de juegos.

hunter
Download Presentation

Juegos de Azar y Programación Declarativa

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Alejandro Pérez Roca Daniel Martín Prieto Jorge Tudela Glez de Riancho Juegos de AzaryProgramación Declarativa

  2. Índice • Introducción. • Reglas de los juegos. • Recopilación y formateo de datos. • Programas de obtención de premios. • Apuestas múltiples. • Diferencias entre los tipos de juegos. • Programas de búsqueda de patrones. • Patrones numéricos. • Patrones visuales. • Resultados curiosos. • Ventajas de utilizar un lenguaje declarativo.

  3. Introducción

  4. 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.

  5. Reglas de los juegos

  6. 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.

  7. 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). • ... 

  8. 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. • Las seis primeras son la combinación ganadora. • La séptima es el complementario. • Por último se extrae el reintegro de un bombo de diez bolas numeradas del 0 al 9.

  9. 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.

  10. 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).

  11. 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.

  12. Recopilaciónyformateo de datos

  13. 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.

  14. Datos originales • De esta forma quedaría el fichero original, el cual tenemos que formatear porque hay información que no nos interesa:

  15. 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. • La primera lista es la apuesta ganadora. • La segunda la forman ventas, bote y acertantes con sus premios correspondientes.

  16. 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 historiaQuiniela let ganancias = calcularPremioHistoria                  ((traducir . words) apuesta) (lines x)         ...         ...

  17. 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] -> Float calcularPremioHistoria _ [] = 0.0 calcularPremioHistoria apuesta (ganadora:premios:restoHistoria) =      calcularPremioDía apuesta (read ganadora) (read premios)     + calcularPremioHistoria apuesta restoHistoria

  18. Programas de obtenciónde premios

  19. Programas de obtención de premios • En esta sección se van a mostrar las funciones necesarias para implementar un programa que: • Reciba como entrada una combinación. • 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.

  20. 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ón let ganancias = calcularPremioHistoria apuesta (lines x) let pérdidas  = fromIntegral ((length . lines) x)/2 * calcularPrecioApuesta apuesta print (ganancias, pérdidas, ganancias - pérdidas)

  21. Código del Euromillón • Cálculo del precio de una apuesta. Apuestas múltiples. calcularPrecioApuesta :: [Int] -> Float calcularPrecioApuesta 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] -> Float calcularPremioHistoria _ [] = 0.0 calcularPremioHistoria apuesta (ganadora:premios:restoHistoria) =  calcularPremioDía apuesta (read ganadora) (read premios) + calcularPremioHistoria apuesta restoHistoria

  22. Código del Euromillón • Cálculo de las ganancias generadas por una apuesta en un día. calcularPremioDía :: [Int] -> [Int] -> [Float] -> Float calcularPremioDía apuesta ganadora premios =  calcularPremioCategoría (categoríaPremio apuesta ganadora) premios • Obtención de la categoría del acierto. categoríaPremio :: [Int] -> [Int] -> Int categoríaPremio apuesta ganadora =  calcularCategoría  (númeroAciertos ((init . init) apuesta) (take 5 ganadora)) (númeroAciertos (drop ((length apuesta)-2) apuesta)         (drop 5 ganadora))

  23. Código del Euromillón • Obtención del número de aciertos. númeroAciertos :: [Int] -> [Int] -> Int númeroAciertos [] _ = 0 nú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 -> Int calcularCategorí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

  24. Código del Euromillón • Obtención del premio obtenido según la categoría. calcularPremioCategoría :: Int -> [Float] -> Float calcularPremioCategoría categoría (ventas:bote:premios) = case categoría of 0  -> 0.0 1  -> 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)

  25. 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

  26. 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)

  27. 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

  28. Código de la Quiniela • En nuestro programa de la Quiniela, la entrada es un String. • 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.

  29. 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] -> Int númeroApuestas [] = 1 nú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).

  30. 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. • No es lo mismo "1 2 x..." que "1 x 2...". númeroAciertos :: [Int] -> [Int] -> Int nú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. • Si apuesta = "x2 1..." y ganadora = "x x..." devolvería un acierto en el primer elemento de las combinaciones.

  31. 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 -> Int calcularCategorí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.

  32. Programas de búsquedade patrones

  33. Búsqueda de Patrones •  ¿Qué es un patrón? • Definición de la RAE: Modelo que sirve de muestra para sacar otra cosa igual. • Nuestra definición: Conjunto de apuestas que tienen algo en común.

  34. Búsqueda de Patrones • Diferenciamos dos tipos de patrones: • Patron numérico: Conjunto de apuestas cuyos números guardan algún tipo de relación matemática. • Ejemplos: • Apuesta con números primos. • Apuesta con números menores que diez. • Apuesta de números pares.    • Patrón visual: Conjunto de apuestas que al ser marcadas sobre el boleto, representan una figura.

  35. Búsqueda de Patrones • Ejemplo: patrones visuales en el boleto de la Quiniela.

  36. 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: • 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. 

  37. 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] -> Float imprimirMedia [] = 0.0 imprimirMedia (combinacion:premios:historia) =  (head . calcularRatio . read) premios +                  imprimirMedia historia

  38. 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.

  39. 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) = do imprimirPatron historia f g n print (combinacion ++ " --> " ++ show((calcularRatio . read) premios))) | otherwise = imprimirPatron historia f g n imprimirPatron [] _ _ _ = putStr ""

  40. 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.

  41. Patrones visuales: Primitiva • Todas las apuestas de un patrón cumplen que: • La distancia de todos los números respecto al primero siempre es la misma. • La distancia en columnas de todos los números  respecto al primero siempre es la misma.

  42. Patrones visuales: Primitiva • Ejemplo: Las dos apuestas pertenecen al mismo patrón.        • [1,11,12,21,22,32] == [17,27,28,37,38,48]

  43. 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)

  44. 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

  45. 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) -> Bool encajaPatron apuesta patron  p = p (foldr (\x y -> max          (numeroAciertos x apuesta) y) 0 patron)

  46. 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.

  47. Patrones visuales: Primitiva • Funciones para crear un boleto y mostrarlo por pantalla. dibujaBoleto :: [Int] -> IO() dibujaBoleto = do putStrLn . ("\n" ++) . creaBoletoVisual  creaBoletoVisual :: [Int] -> String creaBoletoVisual combinacion =      unlines (map unwords (sustituyeBoleto combinacion        "__" (map words (lines _boleto))))

  48. 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 -> String traducirGráfico simb = case simb of           "?"   -> "          \n"           "1"   -> "  *       \n"           "x"   -> "     *    \n"           "2"   -> "        * \n"           "1x"  -> "  *  *    \n"           "12"  -> "  *     * \n"           "x2"  -> "     *  * \n"           "1x2" -> "  *  *  * \n"

  49. 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

  50. 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 = do imprimirPatrón historia patrón error print ((bonita (read combinación)) ++ " --> " ++ show (númeroAciertos patrón (read combinación)) ++ " --> " ++ show (calcularRatio (read premios)))     | otherwise = imprimirPatrón historia patrón error imprimirPatrón [] _ _ = putStr ""

More Related