Let's play some Reaper - Closed for Submissions
Sniper

A bot fueled by spite. Keeps track of opponent's cooldowns and scores. Attempts to keep others from winning. Pretty much never actually wins, but makes the game frustrating to play for others.

EDIT:


If reaping would cause it to win, reap.
If no one is >= 70% of the winning score:


If everyone else is on their cooldown, wait until the last possible moment to reap.
If anyone else would win by reaping the current value, and they are active now or would be active next turn, reap.
If at least half of the other users are on their cooldown, attempt to reap. This makes it difficult to target specific opponents, and so was removed.
Otherwise, reap 25% of the time (essentially to guarantee that this bot does reap SOMETIMES, just in case something weird happens, like everyone is waiting several turns).

If someone IS >= 70% of the winning score:


If Sniper can win a tiebreaker, and next round would be above the average Reap value for the highest scoring opponent, reap
If highest scoring opponent will leave their cooldown next turn, reap.





def Sniper(self, Reap, prevReap):
    # initialize opponents array
    if not hasattr(self.obj, "opponents"):
        self.obj.opponents = {}

    # initialize previous Reap value
    if not hasattr(self.obj, "lastReap"):
        self.obj.lastReap = 0

    # increment all stored wait times to see who will be "active" this turn
    for opponent in self.obj.opponents:
        self.obj.opponents[opponent]["time"] += 1

    # update opponents array
    for opponent in prevReap:
        # don't track yourself, since you're not an opponent
        if opponent != "Sniper":
            # initialize opponent
            if opponent not in self.obj.opponents:
                self.obj.opponents[opponent] = {"time": 0, "points": 0, "num_reaps": 0, "avg": 0}
            self.obj.opponents[opponent]["time"] = 0
            self.obj.opponents[opponent]["points"] += self.obj.lastReap
            self.obj.opponents[opponent]["num_reaps"] += 1
            self.obj.opponents[opponent]["avg"] = self.obj.opponents[opponent]["points"] / self.obj.opponents[opponent]["num_reaps"]

    # done "assigning" points for last round, update lastReap
    self.obj.lastReap = Reap

    # get current 1st place(s) (excluding yourself)
    winner = "" if len(self.obj.opponents) == 0 else max(self.obj.opponents, key=lambda opponent:self.obj.opponents[opponent]["points"])

    # you are ready now
    if self.time &gt;= self.waittime:
        # current Reap is sufficient for you to win
        if self.points + Reap &gt;= self.win:
            return 1

        if (
                # a 1st place exists
                winner != ''
                # if current 1st place is close to winning
                and self.obj.opponents[winner]["points"] / self.win &gt;= .7
        ):
            if (
                    # next round's Reap value will be above opponent's average Reap
                    (Reap * self.mult &gt;= self.obj.opponents[winner]["avg"])
                    # we have been waiting at least as long as our opponent (tiebreaker)
                    and self.time &gt;= self.obj.opponents[winner]["time"]
            ):
                return 1

                # current 1st place opponent will be active next round
            if self.obj.opponents[winner]["time"] + 1 &gt;= self.waittime:
                return 1

        else:
            if (
                    # everyone is waiting for their cooldown
                    all(values["time"] &lt; self.waittime for key, values in self.obj.opponents.items())
                    # and we're tracking ALL opponents
                    and len(self.obj.opponents) == self.lenBots - 1
                    # at least one person will be ready next turn
                    and any(values["time"] + 1 &gt;= self.waittime for key, values in self.obj.opponents.items())
            ):
                return 1

            if (
                    # opponent will be active next round
                    any( (values["time"] + 1 &gt;= self.waittime)
                         # current Reap value would allow opponent to win
                         and (values["points"] + Reap &gt;= self.win) for key, values in self.obj.opponents.items())
            ):
                return 1

            if (
                    # a 1st place exists
                    winner != ''
                    # current 1st place opponent will be active next round
                    and (self.obj.opponents[winner]["time"] + 1 &gt;= self.waittime)
                    # next round's Reap value will be above their average Reap
                    and (Reap * self.mult &gt;= self.obj.opponents[winner]["avg"])

            ):
                return 1

            # # at least half of opponents are waiting for their cooldown
            # if sum(values["time"] &lt; self.waittime for key, values in self.obj.opponents.items()) &gt;= (self.lenBots - 1) / 2:
            #     return 1

            # 25% of the time
            if self.getRandom() &lt;= .25:
                return 1

    # default return: do not snipe
    return 0


Bored

Just for fun, this bot was brought along by a friend and doesn't actually want to be here. They roll a d16 until they get a number in 1-9, then they attempt to reap anytime a number contains the chosen digit. (Going to look for a d10 would disrupt the game, which is rude, and 0 is just too easy!)

def Bored(self, Reap, prevReap):
    # if this is the first round, determine your fav number
    if not hasattr(self.obj, "fav_int"):
        r = 0

        while r == 0:
            # 4 bits are required to code 1-9 (0b1001)
            for i in range(0, 4):
                # flip a coin. Puts a 1 in this bit place 50% of the time
                if self.getRandom() &gt;= .50:
                    r += 2**i
            # if your random bit assigning has produced a number outside the range 1-9, try again
            if not (0 &lt; r &lt; 10):
                r = 0

        self.obj.fav_int = r

    # you are ready now
    if self.time &gt;= self.waittime:
        # current Reap is sufficient for you to win
        if self.points + Reap &gt;= self.win:
            return 1
        # do you like this value?
        if str(self.obj.fav_int) in str(Reap):
            return 1
        # do you like your wait time?
        if self.time % int(self.obj.fav_int) == 0:
            return 1

    # default return: do not reap
    return 0