Do you know "La morra"

Buongiorno a tutti !

Morra is a very old game t learn something about it you can read the wiki or watch this video.

Although it seems a little strange, good and bad players exist as well. The game isn’t based only on chance. Some players detect the week points of adversary and use them.

I had the idea, just for fun, to simulate all of this .

So I simulate matches between Luigi and Lorenzo using the most common rules (totals of 0 and one accepted) 15 points to win a set. I chained series of 100 sets (not very realistic) just for statistics.

What are the mistakes of a poor player ?

Not too many variations in the number of fingers thrown. Some show only a few fingers others prefer full hands. These are special cases of a more general situation where a player restricts by will or not to some intervals of [0,1,2,3,4,5].

Other mistakes are about bids some players are ‘overbidding ‘ always waiting a great number of fingers from the opponent. For some others it’s the contrary. If you know this you can use it to prevent him from winning easily, but in the end he can notice.

More subtle cases are in the conditional probabilities some players will throw out 4 after 3 almost without thinking, if you know it you can use it.

Now the idea is this.

Lorenzo will play the stupid one and Luigi, first with our help, then (I hope) alone will play the smart one.

We can begin with very simple cases when we force Lorenzo to play only with two fingers showing only 0,1 and 2 and never 3,4,5.

Actually the Shannon quantity of information to know the good guess is log2(6) and will drop to log2(3). We can expect the result to be in favour of Luigi in the same proportion if he notices the fact…

Maybe some of you can be interested in this projects, give ideas, check code, propose changes, and so on…would be good to have an evolution toward self learning and adaptation of code by meta-programming but my ideas aren’t yet very clear at the moment.

So everything starts with this code which corresponds to a totally fair game between two opponents each of them knowing nothing about the other.
The probability of winning should be around 50%, experience confirms this :

# basic data for a player
struct Player
    id::Int8 # not in use for now just in case of further tornament
    name::String  # first name
end

# the 'game' data
struct Game
    hand1::Int8 # first player hand
    guess1::Int8 #first player guess for sum
    hand2::Int8 #same for second player
    guess2::Int8
    sum::Int8 #sum of both hands
    wins1::Bool #victory of player 1 set to true
    wins2::Bool #same for second player
end

# a set is a vector of games of undefined length (minimum 15)
#but can be much longer specially in case of 'duce'
mutable struct Set
    history::Vector{Game}
end

# a 'match' consists in 100 ''sets'
mutable struct Match
    results::Vector{Int8}
end

#for now routine is the same for both players
# and doesn't take into account any extra information'
function plays(P::Player)
    hand = Int8(rand(0:5))
    guess = hand + Int8(rand(0:5))
    return hand, guess
end

#Both boys from the beautiful Corsica
Luigi , Lorenzo = Player(1, "Luigi") , Player(2, "Lorenzo")

#both can win together at the same game
function game(S)
    h1, g1 = plays(Luigi)
    h2, g2 = plays(Lorenzo)
    s = h1 + h2
    w1, w2 = false, false
    #tests to decide if there's one or two winners
    w1 , w2 = s==g1 , s==g2
    #store the result in temporary list
    push!(S.history, Game(h1, g1, h2, g2, s, w1, w2))
end

# They play in the same set till one of them reaches 15 victories alone
# if they reach 15 together the stop is increased to 16 and so on
#there must be one and only one winner for every set.
function playset()
    # to select a possible winner for the set
    scores(S::Set) =(reduce(+ ,(g.wins1 for g in S.history)),reduce(+, (g.wins2 for g in S.history)))
    S , stop = Set([]) , 15 # empty history and stop fixed to 15 to begin with
    while true
        game(S)
        w1, w2 = scores(S)
        if w1 == stop && w2 < stop
            return (Int8(1), Int8(0))
            break
        end
        if w2 == stop && w1 < stop
            return (Int8(0), Int8(1))
            break
        end
        if w1 == stop && w2 == stop
            stop += 1
        end
    end
end

# a 'match' consists in 100 consecutive 'sets'
function match()
    M = Match([Int8(0), Int8(0)])
    for i = 1:100
        M.results[1]+=playset()[1] ;  M.results[2]+=playset()[2]
    end
    # the winner is the one with the greatest number of victories
    # equality (50-50) is in theory possible
    return M.results
end

#let's go!
println(match())


5 Likes

Error in match function to be replaced by :

# a 'match' consists in 100 consecutive 'sets'
function match()
    M = Match([Int8(0), Int8(0)])
    for i = 1:100
        p=playset()
        M.results[1]+=p[1] ;  M.results[2]+=p[2]
    end
    # the winner is the one with the greatest number of victories
    # equality (50-50) is in theory possible
    return M.results
end

Next step , we don’t allow Lorenzo to use more than 2 fingers, and Luigi doesn’t know and doesn’t notice anything. This situation described by the new ‘plays’ function ::

#for now routine is the same for both players
# and doesn't take into account any extra information'
function plays(P::Player)
    if P.id==2 #Lorenzo never shows more than 2 fingers
        hand = Int8(rand(0:2))
        guess = hand + Int8(rand(0:5))
    else
        hand = Int8(rand(0:5)) #and Luigi knows nothing about it and doesn't notice either
        guess = hand + Int8(rand(0:5))
    end
    return hand, guess
end

About results of a match, nothing changed you can check 50%-50%
Step n°3
We inform Luigi about the behaviour of Lorenzo and he takes this into account

function plays(P::Player)
    if P.id==2 #Lorenzo never shows more than 2 fingers
        hand = Int8(rand(0:2))
        guess = hand + Int8(rand(0:5))
    else
        hand = Int8(rand(0:5)) #Luigi knows the fact
        guess = hand + Int8(rand(0:2)) # and uses in his own bid
    end
    return hand, guess
end

Of course Luigi has now a great advantage, without test I estimated it would be in proportion log(6)/log(3).
My mistake !
Things are much simpler Lorenzo has to guess among 6 possibles values and Luigi in only three, so the number of chances simply doubles, as the experience demonstrates :

#let's see what happens
function check()
    S=Set([])
    for i in 1:1000
        game(S)
    end
    return S.history
end

L=check()
n1=count(g.wins1==true for g in L)
n2=count(g.wins2==true for g in L)
println(n1/n2)

Of course this accumulates with games, chances of Lorenzo to win a full match drop down to only 1 or 2%