# Three Number Monte

## The Rules

In this game, each round, every player faces off against every other player in a round robin format. In each match, players pick 3 positive integers that sum to 360. Let's say player 1 picks `[a, b, c]`

and player 2 picks `[x, y, z]`

Now `a`

is compared to `x`

, `b`

is compared to `y`

, and `c`

is compared to `z`

. If `a > x`

, player 1 gets a tally, and vice versa, for all 3 numbers.

If either player gets at least 2 tallies, they win the match and receive 1 point - otherwise, both players receive half a point.

To make this game more interesting, each match, the players get a `data`

object that they can use for memory, and also a `history`

object that contains all of their opponent's previous choices.

## Submission

Write a Python function that takes in two arguments, `data`

and `history`

, described above, and returns an array of positive integers that sums to 360. You can find some example players in the driver code.

## Driver

```
import random, itertools, math
def onethird(data, history):
a = [120, 120, 120]
random.shuffle(a)
return a
def onehalf(data, history):
a = [180, 180, 0]
random.shuffle(a)
return a
def rand(data, history):
x = random.randint(0, 360)
y = random.randint(0, 360 - x)
return [x, y, 360 - x - y]
players = [onethird, onehalf, rand]
#Game logic
ROUNDS = 10000
pairs = list(itertools.combinations(players, 2))
data = {}
history = {}
scores = {}
for p in players:
data[p] = {}
history[p] = []
scores[p] = 0
for _ in range(ROUNDS):
random.shuffle(pairs)
for pair in pairs:
p1, p2 = pair
a1 = p1(data[p1], history[p2])
a2 = p2(data[p2], history[p1])
assert sum(a1) == 360, (p1, a1)
assert sum(a2) == 360, (p2, a2)
assert all(isinstance(i, int) for i in a1), (p1, a1)
assert all(isinstance(i, int) for i in a2), (p2, a2)
assert all(0 <= i <= 360 for i in a1), (p1, a1)
assert all(0 <= i <= 360 for i in a2), (p2, a2)
history[p1].append(a1)
history[p2].append(a2)
p1c, p2c = 0, 0
for i in range(3):
if a1[i] > a2[i]:
p1c += 1
elif a2[i] > a1[i]:
p2c += 1
if p1c >= 2:
scores[p1] += 1
elif p2c >= 2:
scores[p2] += 1
else:
scores[p1] += 0.5
scores[p2] += 0.5
print(scores)
```

The tentative deadline for this competition is **8/16/21 00:00 UTC**.

## 2 answers

## MoshiBot

```
from numpy.polynomial import Polynomial
def MoshiBot(data, history):
if len(history) < 10:
return [180, 180, 0]
lastmin = min(history[-1])
lastmid = sorted(history[-1])[1]
projectedmin = lastmin
projectedmid = lastmid
if len(history) >= 10:
cutoff = len(history) - 10
snapshot = history[cutoff:]
minvalues = list(map(min, snapshot))
midvalues = list(map(lambda vals: sorted(vals)[1], snapshot))
xpoints = range(cutoff, cutoff + len(snapshot))
minprojection = Polynomial.fit(xpoints, minvalues, deg=2)
midprojection = Polynomial.fit(xpoints, midvalues, deg=2)
projectedmin = minprojection(len(history))
projectedmid = midprojection(len(history))
safemin = max(0, min(360, math.ceil(projectedmin + 1)))
if safemin >= 180:
# The model broke down
return [180, 180, 0]
mid = math.floor(180 - safemin / 2)
if mid > projectedmid:
return [safemin, mid, 360 - safemin - mid]
else:
mid = max(safemin, min(360 - safemin, math.ceil(projectedmid + 1)))
return [safemin, mid, 360 - safemin - mid]
```

Attempts to predict the opponents next move using a regressive model.

#### 1 comment thread

# The "Uppers"

```
def oneup(data, history):
if len(history) > 0:
prev = history[-1]
if prev[2] > 1:
better = [prev[0] + 1, prev[1] + 1, prev[2] - 2]
elif prev[1] > 1:
better = [prev[0] + 1, prev[1] - 2, prev[2] + 1]
else:
better = [prev[0] - 2, prev[1] + 1, prev[2] + 1]
random.shuffle(better)
return better
return [[180, 180, 0], [180, 0, 180], [0, 180, 180]][random.randint(0, 2)]
def avgup(data, history):
if 't1' not in data:
data['t1'] = 0
data['t2'] = 0
data['t3'] = 0
if len(history) > 0:
prev = history[-1]
prev.sort()
data['t1'] += prev[0]
data['t2'] += prev[1]
data['t3'] += prev[2]
a1 = data['t1'] / len(history)
a2 = data['t2'] / len(history)
a3 = data['t3'] / len(history)
if math.ceil(a1) + math.ceil(a2) <= 360:
better = [math.ceil(a1), math.ceil(a2), 360 - math.ceil(a1) - math.ceil(a2)]
else:
better = [math.ceil(a1), 360 - math.ceil(a1), 0]
random.shuffle(better)
return better
return [[180, 180, 0], [180, 0, 180], [0, 180, 180]][random.randint(0, 2)]
```

Two bots based off a single concept that seem to perform reasonably well. One takes the last submission the opponent made, and "one-ups" it - increments 2 values by 1 and decrements 1 value by 2. The other does the same thing, except with the average of all previous submissions by the opponent.

## 0 comment threads