> Progress: Updated the rules after some discussions in The Nineteenth Byte.

#Sylver Coinage KotH [tag:king-of-the-hill][tag:number-theory][tag:python]

[Sylver Coinage][1] is a 2-player mathematical game that has the following rules:

1. Two players take turns announcing a natural number each time.
2. Each number announced must be unrepresentable as the sum of non-negative multiples of the numbers announced before.
> Eg. if the first three numbers announced are \$\{6, 11, 15\}\$, then you cannot announce any numbers representable as \$6n_1+11n_2+15n_3\$, where \$n_1,n_2,n_3\ge0\$. You can announce, for example, \$16\$, though.

3. The player who announced a number not complying with Rule 2, or the number 1, loses.

Here is a twist -- R. L. Hutchings proved that announcing a prime number as the first play provides a winning strategy for the first player, although the detail of the strategy is not yet known. <s>So I put a restriction here: **the first player cannot announce a prime number in the first step.**</s> Now the first two numbers will be generated randomly by the driver at the beginning and will be coprime. No more restriction on prime numbers now.

##Technical Information

A bot playing the game will have to implement a Python 3 class with two methods: `announce()` and `learn()`. `announce()` should receive a list of numbers (possibly empty) and return a single integer, and `learn()` should receive two integers (id of the first move and second move) and the complete list of the numbers in the last game played. 

Here is a sample implementation. **Note: DO NOT use this as your submission -- this sample only serves as a demonstration, and it may announce numbers that violate Rule 2.**


<!-- language-all: lang-python -->

    class SampleBot:
        def __init__(self, id):
            self.id = id
    
        def announce(self, list):
            import random
            return random.randint(1, 101)

        def learn(self, first, second, list):
            pass

###[Test Drive](https://tio.run/##nVTBjtowEL37K0asKtlLlk3gVAQ99NBjL@0NRZFJzGLJsWliiBDi2@nYCUkILFIbCQj2zHtvnme8O9qt0bPLJVW8LOEXz3dKfDd2TgCfTGwgSaSWNkloKdQmAJmxes89bmkiM1jiMmkzuNZmr1PRZChZ2l6OzHemsFBwnZm8XS2E3Re6WZ24H6ktjQKIQnwiRsgLHERxBKnFZiNTKbQljkyWCpd4kZp8TTWyNVRyA0poqnrMG1OARADH8iFoGICGMSCHWoVxL65Jl7Bcgr5d7kldaXh/96nxTYhQLTV8g@g@HxU7w250wxtIpyOax@w@wVd5D9QXI1sxWBJGk97mT6MF8WZ9pBnlAaw7jzgsYD0fHoOLWwfAay2@Im3sg0DeBJTiUwz4gnykOatdIXNBdcevcXvqjA7vAH5whK3j0K@Zf6u2Ugn8/4qfxc3pNFhygPUQr8YcL2Hat@l3sUeblCkF0r1FxC8iGNH7vMSXVUzWxvq3dkxoyIJuaGjEYnhBvJ3iqYBK2i0czb4An7cVhZgQctv/dUHU9YujYXgc08adK@1wJtxAYO9@9Q/SP9@Pr06783CQ2COBx15F8aBDu0JrWd6LxdXOA265QlbOmHjSTrnX7SPwdFHA6BSe6Sk6s/YiyOA0PY8mOH85t9Qed4J2QGySJJrnIkmCPrzMAjiwGrcazsuhroBda6t6V5ItMHo0ejL1rdkPZv5@yhwgtspoPCKPlrHYVyy2rc47K9HiCr/ra2ThGSvm5wTCbsCf@gV2yy2WDeLPniuwBk6z/zIxcFpZx9p0uIvoxvvg5qbXCp9Li/5ZwxNu33Fo4@pQ92kzcxHehz5mKKOSunzA7@Id9L2GdsfruFz@Ag)

<!-- language-all: lang-python -->

    class SampleBot:
        def __init__(self, id):
            self.id = id
    
        def announce(self, list):
            import random
            return random.randint(1, 100001)

        def learn(self, first, second, list):
            pass
    
    # very inefficient
    def islinearcomb(n, l):
        if len(l):
            for i in range(0, n + 1, l[0]):
                if i == n:
                    return [n // l[0]]
                elif len(l) > 1:
                    isl = islinearcomb(n - i, l[1:])
                    if isl:
                        return [i // l[0]] + isl
        return None
    
    def gcd(a, b):
        if a < b:
            return gcd(b, a)
        elif not b:
            return a
        else:
            return gcd(b, a % b)
    
    def isprime(n):
        if n % 2 == 0:
            return False
        i = 3
        while i * i <= n:
            if n % i == 0:
                return False
            i += 2
        return True
    
    lose = -1
    turn = 0
    nums = []
    bots = [SampleBot(0), SampleBot(1)] # replace with your bots here.
    
    import random
    while (len(nums) < 2):
        nums = [random.randint(100000, 999999), random.randint(100000, 999999)]
        if gcd(nums[0], nums[1]) > 1:
            nums = []
    while lose < 0:
        v = bots[turn].announce(nums)
        print("{0}({1}) announced {2}".format(type(bots[turn]).__name__, bots[turn].id, v))
        w = islinearcomb(v, nums)
        if w:
            str = ""
            for i in range(0, len(nums)):
                if i:
                    str += "+"
                str += "{0}*{1}".format(nums[i], w[i] if i < len(w) else 0)
            print("{0}({1}) announced {2} that is equal to {3}".format(type(bots[turn]).__name__, bots[turn].id, v, str))
            lose = turn
        elif v == 1:
            print("{0}({1}) announced 1".format(type(bots[turn]).__name__, bots[turn].id))
            lose = turn
        nums += [v]
        turn = 1 - turn
    print("{0}({1}) wins".format(type(bots[1 - lose]).__name__, bots[1 - lose].id))

##Restrictions

Both methods should be finished promptly within 20 seconds `todo: adjustments`. Failing to finish a method within the requirement time will lead to disqualification and rerun of all 100 rounds with the remaining bots.

##Schedule

Submissions will be open until `todo: date here`. After that 100 complete round-robin rounds will be done. Each pair of bots will compete twice in each round, one with the first bot announcing first, and one with the second bot announcing first. Each win brings 3 points, each draw brings 1 point, and each lose brings no points. The bot with the highest points after 100 rounds wins. The tiebreaker will be as follows:

1. Points got
2. Wins achieved
3. Drawing lots

  [1]: https://en.wikipedia.org/wiki/Sylver_coinage