Em Tempos de Copa do Mundo…

Há quem ache — e eu concordo — que a UEFA Champions League é um torneio mais relevante do que a Copa do Mundo, mas não podemos negar que o Mundial mexe com a nossa rotina. Mesmo quem não é apaixonado pelo esporte passa a prestar atenção nas seleções, nas partidas, na festa.

E com a Copa do Mundo vem os bolões. Muitos bolões.

Todo mundo já sabe como funciona um bolão, embora a mecânica possa mudar de um lugar para outro:

  1. Cada palpite tem um valor fixo (ex: R$2,00).
  2. Cada um pode dar quantos palpites quiser.
  3. No final, o montante arrecadado é dividido igualmente entre os vencedores. Se não houverem vencedores: (a) o bolão “acumula”1, ou (b) todo mundo recebe seu dinheiro de volta, ou (c) sua-regra-maluca-aqui.

Esse tipo de bolão é interessante, mas ele podia ser melhor em alguns aspectos.

Primeiro, o organizador acaba perdendo muito tempo correndo atrás de troco, afinal não é todo mundo que anda com dinheiro trocado na carteira. Além disso, valor fixo de palpite é muito chato, pois o valor arrecadado é dividido por igual entre os ganhadores. O bolão fica muito mais interessante quando o valor do palpite é variável e o valor arrecadado é dividido proporcionalmente entre os ganhadores. Assim, no caso de vários ganhadores, ganha mais quem apostar mais.

O problema de se fazer bolões com palpite variável é ter que ficar calculando o valor proporcional a ser dado a cada ganhador. Portanto eis o desafio: criar um programa que receba uma lista com todos os palpites e retorne uma lista com os palpites vencedores juntamente com o valor proporcional a ser pago a cada ganhador.

E aí, topa?

Resolvendo o problema… em Clojure

Como escrever mais código do que necessário está saindo de moda, optei por codificar uma possível solução em Clojure. Por ser uma linguagem funcional, Clojure é excelente para esse tipo de tarefa. Basta iniciar o REPL e mãos à obra!

Primeiramente, devemos definir como um palpite deve ser representado:

(defstruct bet :name :amount :guess :rate)

Todos os palpites possuem o mesmo conjunto de informações, por isso definimos um StructMap com as chaves :name, :amount, :guess e :rate.

Abaixo, uma possível lista de palpites:

(def pool
     [(struct bet "Daniel"  2.0 [2 0]) ;; Daniel aposta R$2,00 no placar 2x0
      (struct bet "Antônio" 4.0 [0 0]) ;; Antônio aposta R$4,00 no placar 0x0
      (struct bet "Marcela" 1.0 [2 0]) ;; Marcela aposta R$1,00 no placar 2x0
     ])

Certo, já sabemos como os palpites são representados. Agora, como saber o montante total de uma lista de palpites (a.k.a. bolão)? Simples, basta calcular o somatório dos valores de todos os palpites:

(defn pool-amount [poolseq]
  (reduce + (map :amount poolseq)))
 
;; testando
(pool-amount pool)
=> 7.0

Beleza, mas quanto cada palpite representa em relação ao total arrecadado? Afinal, precisamos calcular o valor proporcional a ser pago a cada ganhador.

Sem problemas, basta dividir o valor de cada palpite pelo montante total:

(defn pool-rates [poolseq]
  (let [sum (pool-amount poolseq)]
    (map #(assoc % :rate (/ (:amount %) sum)) poolseq)))
 
;; testando
(map :rate (pool-rates pool))
=> (0.285 ..., 0.571 ..., 0.142 ...)
 
(reduce + (map :rate (pool-rates pool)))
=> 1.0

Repare que, se todos os participantes ganhassem um bolão, o valor recebido pelo Antônio seria o dobro do valor recebido pelo Daniel, que seria o dobro do valor recebido pela Marcela. Justiça, finalmente!

Pronto, agora só falta uma função para filtrar os ganhadores da lista de participantes e calcular o valor proporcional a ser pago a cada um deles:

(defn pool-winners [poolseq result]
  (let [sum (pool-amount poolseq)
        winners (filter #(= (:guess %) result) poolseq)]
    (map #(assoc % :amount (double (* (:rate %) sum))) (pool-rates winners))))
 
;; testando
(pool-winners pool [1 0]) ;; sem ganhadores
=> ()
 
(pool-winners pool [0 0]) ;; apenas um ganhador
=> ({:name "Antônio", :amount 7.0, :guess [0 0], :rate 1.0})
 
(pool-winners pool [2 0]) ;; vários ganhadores
=> ({:name "Daniel", :amount 4.66 ..., :guess [2 0], :rate 0.66 ...}
    {:name "Marcela", :amount 2.33 ..., :guess [2 0], :rate 0.33 ...})

Uma grande vantagem dessa solução é que ela permite que você aposte em qualquer coisa. Quer apostar no sexo de um bebê logo após a boa notícia (ou má notícia, sei lá)?

;; :male = masculino, :female = feminino
(def pool [(struct bet "Daniel"  3.0 :male)
           (struct bet "Antônio" 5.0 :female)
           (struct bet "Fran"    1.0 :female)])
 
(pool-winners pool :female) ;; testando
=> ({:name "Antônio", :amount 7.5, ...} {:name "Fran", :amount 1.5, ...})

Aí está, uma solução simples para um problema simples. Como deveria ser sempre.

Minha solução, na íntegra

Embora seja possível resolver isso com um one-liner em Lisp/Clojure (afinal o Reader não trabalha com linhas, mas com estruturas de dados), eu optei por escrever um código que fosse o mais legível possível para quem não conhece ou está aprendendo Clojure:

(defstruct bet :name :amount :guess :rate)
 
(defn pool-amount [poolseq]
  (reduce + (map :amount poolseq)))
 
(defn pool-rates [poolseq]
  (let [sum (pool-amount poolseq)]
    (map #(assoc % :rate (/ (:amount %) sum)) poolseq)))
 
(defn pool-winners [poolseq result]
  (let [sum (pool-amount poolseq)
        winners (filter #(= (:guess %) result) poolseq)]
    (map #(assoc % :amount (double (* (:rate %) sum))) (pool-rates winners))))

Levando adiante

Você programa em Haskell? Python? Ruby? Brainfuck? Qual seria a sua solução para este problema de extrema importância para o desenvolvimento da raça humana? :P

Atualização (03/11/2010). Solução em Common Lisp, usando copy-* para evitar mutabilidade:

(defstruct bet name amount guess rate)
 
(defun pool-amount (pool)
  (reduce #'+ (mapcar #'bet-amount pool)))
 
(defun pool-rates (pool)
  (let ((sum (pool-amount pool)))
    (mapcar (lambda (bet)
              (let ((x (copy-bet bet)))
                (setf (bet-rate x) (/ (bet-amount x) sum))
                x)) pool)))
 
(defun pool-winners (pool result)
  (let ((sum (pool-amount pool))
        (winners (loop for bet in pool when (equal (bet-guess bet) result) collect bet)))
    (mapcar (lambda (winner)
              (let ((x (copy-bet winner)))
                (setf (bet-amount x) (* (bet-rate x) sum))
                x)) (pool-rates winners))))
  1. Todo o montante recebido continua disponível no próximo bolão, mas os participantes devem pagar um novo palpite.

Sobre Daniel Martins

Fundador da Destaquenet, ele é graduado em Sistemas de Informação e desenvolve softwares como hobby e profissão desde 2000. Especializado na plataforma Java, ele utiliza a tecnologia há vários anos, sendo programador e desenvolvedor web certificado pela Sun Microsystems, recentemente adquirida pela Oracle. Também se interessa por assuntos ligados à cultura open source, metodologias ágeis, engenharia de software, frameworks e linguagens dinâmicas tais como Python, Ruby e Smalltalk.
Esta entrada foi publicada em Português, Programação e marcada com a tag , , , , , , , , . Adicione o link permanente aos seus favoritos.

Deixe um Comentário

O seu endereço de email não será publicado Campos obrigatórios são marcados *

*

Você pode usar estas tags e atributos de HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="" highlight="">