Re: Programming challenges
Posted: Mon Jan 23, 2023 11:53 pm
so far commodorejohn is the only one who has submitted code.
come on guys! get on it!
come on guys! get on it!
Code: Select all
def primesin(i, mymoves, myopp):
a = math.sin(math.radians(i * 173))
b = math.sin(math.radians(i * 113))
d = a + b
if d >= 0.4:
return 1
if d <= -0.4:
return 2
return 0
Code: Select all
def primes(i, mymoves, myopp):
a = i * 23
b = i * 37
d = a ^ b
z = (d & 2) | ((d & 128) >> 7)
while z == 3:
d >>= 1
z = (d & 2) | ((d & 128) >> 7)
return z
Code: Select all
def determinism(i, mymoves, myopp):
bits = 0 # assume nothing
for x in myopp: # carefully study the opponent's moves
z = x + 1 # consider the value of nothing
for y in range(0, 2): # handle each bit with care
bits <<= 1 # do a little dance
bits |= (z & 1) # make a little love
z >>= 1 # get down tonight
if bits & 65536: # if it's heads,
bits ^= 10410 # take a shot
bits &= 65535 # all things in moderation
z = (bits >> 14) & 3 # plan our move carefully
while z == 3: # but if it's illegal,
bits <<= 2 # continue planning
z = (bits >> 14) & 3 # until we arrive at the correct answer
return z
Code: Select all
import math
__doc__ = '''
Submission for round 2 of the "Programming challenges" forum game,
organized by phillip1882 at ramenchef.net/nxf.
Author: Jplus
Usage: at the start of each match, create a new instance of the final
strategy:
playAsJplus = JplusStrategy()
This instance can be called at every turn, as specified in the task
description:
next_move = playAsJplus(i, mymoves, myopp)
'''
from operator import itemgetter, methodcaller
ROCK, PAPER, SCISSORS = 0, 1, 2
LOSS, TIE, WIN = 2, 0, 1
ROUND_MISMATCH_MESSAGE = '''
The round number is less than the expected round number. Did you
create a new instance of the strategy before starting the match?
'''
def outcome(ours, theirs):
""" Given our and their moves, what is the outcome for us? """
return (ours - theirs) % 3
def beat(theirs):
""" Given their move, what should be played to beat it? """
return (theirs + 1) % 3
def blank_tally():
""" Empty frequency table for initializing a tally. """
return {ROCK: 0, PAPER: 0, SCISSORS: 0}
def most_frequent(tally):
""" Given a frequency table, return the most prevalent move. """
return max(tally.items(), key=itemgetter(1))[0]
class Strategy(object):
"""
Base class for playing strategies.
A strategy is initialized once for each match and can then be called as a
function for each turn. The call signature is (round_number,
opponent_moves, own_moves), as specified in the challenge description of
round 2. The use of a callable class instance instead of a bare function
makes it possible to build the opponent model incrementally, so that moves
can be decided efficiently even after many turns.
A child class must at least define a predict method, which attempts to
guess what the opponent will play. This method takes no arguments and
should base its answer purely on an internal model, however simple. The
move that ends up being played is always beat(self.predict()).
Optionally, child classes may also override the observe method in order to
build their internal model. This method takes two arguments: the opponent's
move in the previous turn, and the own move in the previous turn. Any
return vaue is ignored.
All playing strategies keep track of how many turns they have seen and how
accurate their prediction of the opponent's moves has been so far. This is
taking care of automatically in the __call__ method.
"""
def __init__(self):
self.turn = 0
self.score = 0
def __call__(self, round, their_history, our_history):
assert round == self.turn, ROUND_MISMATCH_MESSAGE
theirs, ours = [],[]
if round > 0:
theirs, ours = their_history[-1], our_history[-1]
self.observe(theirs, ours)
previous_result = outcome(ours, theirs)
if previous_result == LOSS:
self.score -= 1
elif previous_result == WIN:
self.score += 1
guess = theirs
self.turn += 1
if round == 0:
return 0
return beat(guess)
def weighted_call(self, round, their_history, our_history):
move = self(round, their_history, our_history)
return self.accuracy(), move
def observe(self, theirs, ours):
pass
def accuracy(self):
if self.turn < 1:
return 0
return self.score / self.turn
def arbitrary(self):
return self.turn % 3
def reflect(base):
""" Given a statistical strategy, return a strategy that counters it. """
class Reflective(base):
def observe(self, theirs, ours):
super().observe(ours, theirs)
def predict(self):
their_prediction = super().predict()
return beat(their_prediction)
Reflective.__name__ += base.__name__
return Reflective
class MarkovChain(Strategy):
"""
Base the move on a pattern frequency analysis of the opponent.
"""
def __init__(self, stride=4):
super().__init__()
self.stride = stride
self.window = ()
self.tally = {}
def observe(self, theirs, ours):
if self.turn >= self.stride:
tally = self.tally.get(self.window, blank_tally())
tally[theirs] += 1
self.tally[self.window] = tally
self.window = (*self.window[1:], theirs)
else:
self.window = (*self.window, theirs)
def predict(self):
tally = self.tally.get(self.window)
if tally is None:
return self.arbitrary()
return most_frequent(tally)
ReflectiveMarkovChain = reflect(MarkovChain)
class JplusStrategy(Strategy):
""" Counter both of the above strategies. """
def __init__(self):
super().__init__()
self.candidates = [
MarkovChain(),
ReflectiveMarkovChain(),
]
def __call__(self, round, their, our):
# Update this strategy's accuracy, ignoring the prediction.
super().__call__(round, their, our)
# Defer the actual move to the best performing candidate.
forward = methodcaller('weighted_call', round, their, our)
return max(map(forward, self.candidates), key=itemgetter(0))[1]
def predict(self):
pass
Code: Select all
def runTourney():
Left =["primesin","playAsJplus"]
Right =[ "determinism", "primes"]
cycle = 0
totalWins ={"determinism": 0, "primesin":0, "primes":0,"playAsJplus":0}
totalRounds = {"primesin":0, "primes":0, "determinism": 0, "playAsJplus":0}
while cycle < 2*len(Left)-1:
for i in range(0,len(Left)):
print(Left[i],Right[i])
if Left[i] == "playAsJplus" or Right[i] =="playAsJplus":
playAsJplus = JplusStrategy()
mymovesPlay1 = []
mymovesPlay2 = []
wins = []
if Left[i] == "BYE" or Right[i] == "BYE":
continue
for j in range(0,5000):
if j == 0:
result1 = eval(Left[i]+"(0 ,[], [])")
result2 = eval(Right[i]+"(0 ,[], [])")
if result1 =="R":
result1 = 0
if result1 =="P":
result1 = 1
if result1 =="S":
result1 = 2
if result2 =="R":
result2 = 0
if result2 =="P":
result2 = 1
if result2 =="S":
result2 = 2
mymovesPlay1 += [result1]
mymovesPlay2 += [result2]
if result1 == result2:
wins +=[0.5,0.5]
totalWins[Left[i]]+= 0.5
totalWins[Right[i]]+= 0.5
elif (result1 +1)%3 == result2:
wins += [0,1]
totalWins[Right[i]]+= 1
else:
wins += [1,0]
totalWins[Left[i]]+= 1
continue
result1 = eval(Left[i]+"("+str(j)+","+"mymovesPlay1, mymovesPlay2)")
result2 = eval(Right[i]+"("+str(j)+","+"mymovesPlay2, mymovesPlay1)")
if result1 =="R":
result1 = 0
if result1 =="P":
result1 = 1
if result1 =="S":
result1 = 2
if result2 =="R":
result2 = 0
if result2 =="P":
result2 = 1
if result2 =="S":
result2 = 2
mymovesPlay1 += [result1]
mymovesPlay2 += [result2]
if result1 == result2:
wins[0] += 0.5
wins[1] += 0.5
totalWins[Left[i]]+= 0.5
totalWins[Right[i]]+= 0.5
elif (result1 +1)%3 == result2:
wins[1] += 1
totalWins[Right[i]]+= 1
else:
wins[0] += 1
totalWins[Left[i]]+= 1
if wins[0] > wins[1]+50:
totalRounds[Left[i]]+= 1
elif wins[0]+50 < wins[1]:
totalRounds[Right[i]] +=1
else:
totalRounds[Left[i]]+= 0.5
totalRounds[Right[i]] +=0.5
value = Left.pop(1)
Right.insert(0,value)
value = Right.pop(-1)
Left += [value]
cycle += 1
print(totalRounds)
print(totalWins)
runTourney()
Code: Select all
__doc__ = '''
Submission for round 2 of the "Programming challenges" forum game,
organized by phillip1882 at ramenchef.net/nxf.
Author: Jplus
Usage: at the start of each match, create a new instance of the final
strategy:
playAsJplus = JplusStrategy()
This instance can be called at every turn, as specified in the task
description:
next_move = playAsJplus(i, mymoves, myopp)
'''
from operator import itemgetter, methodcaller
ROCK, PAPER, SCISSORS = 0, 1, 2
LOSS, TIE, WIN = 2, 0, 1
INTERPRETATION = {
'r': ROCK,
'p': PAPER,
's': SCISSORS,
'0': ROCK,
'1': PAPER,
'2': SCISSORS,
}
REPRESENTATION = ['rock', 'paper', 'scissors']
ROUND_MISMATCH_MESSAGE = '''
The round number is less than the expected round number. Did you
create a new instance of the strategy before starting the match?
'''
def outcome(ours, theirs):
""" Given our and their moves, what is the outcome for us? """
return (ours - theirs) % 3
def beat(theirs):
""" Given their move, what should be played to beat it? """
return (theirs + 1) % 3
def beaten(ours):
""" Given our move, what opponent move would have been beaten? """
return (ours - 1) % 3
def same(value):
""" An identity function. """
return value
def blank_tally():
""" Empty frequency table for initializing a tally. """
return {ROCK: 0, PAPER: 0, SCISSORS: 0}
def most_frequent(tally):
""" Given a frequency table, return the most prevalent move. """
return max(tally.items(), key=itemgetter(1))[0]
class Strategy(object):
"""
Base class for playing strategies.
A strategy is initialized once for each match and can then be called as a
function for each turn. The call signature is (round_number,
opponent_moves, own_moves), as specified in the challenge description of
round 2. The use of a callable class instance instead of a bare function
makes it possible to build the opponent model incrementally, so that moves
can be decided efficiently even after many turns.
A child class must at least define a predict method, which attempts to
guess what the opponent will play. This method takes no arguments and
should base its answer purely on an internal model, however simple. The
move that ends up being played is always beat(self.predict()).
Optionally, child classes may also override the observe method in order to
build their internal model. This method takes two arguments: the opponent's
move in the previous turn, and the own move in the previous turn. Any
return vaue is ignored.
All playing strategies keep track of how many turns they have seen and how
accurate their prediction of the opponent's moves has been so far. This is
taking care of automatically in the __call__ method.
"""
def __init__(self):
self.turn = 0
self.score = 0
def __call__(self, round, their_history, our_history):
assert round == self.turn, ROUND_MISMATCH_MESSAGE
if round > 0:
theirs, ours = their_history[-1], our_history[-1]
self.observe(theirs, ours)
previous_result = outcome(ours, theirs)
if previous_result == LOSS:
self.score -= 1
elif previous_result == WIN:
self.score += 1
guess = self.predict()
self.turn += 1
return beat(guess)
def weighted_call(self, round, their_history, our_history):
move = self(round, their_history, our_history)
return self.accuracy(), move
def observe(self, theirs, ours):
pass
def accuracy(self):
if self.turn < 1:
return 0
return self.score / self.turn
def arbitrary(self):
return self.turn % 3
def reflect(base):
""" Given a statistical strategy, return a strategy that counters it. """
class Reflective(base):
def observe(self, theirs, ours):
super().observe(ours, theirs)
def predict(self):
their_prediction = super().predict()
return beat(their_prediction)
Reflective.__name__ += base.__name__
return Reflective
class Constant(Strategy):
""" A strategy that always repeats the same arbitrary move. """
def __init__(self, move=ROCK):
super().__init__()
self.prediction = beaten(move)
def predict(self):
return self.prediction
class AntiConstant(Strategy):
""" Assume that the opponent is biased towards a particular move. """
def __init__(self):
super().__init__()
self.tally = blank_tally()
def observe(self, theirs, ours):
self.tally[theirs] += 1
def predict(self):
return most_frequent(self.tally)
ReflectiveAntiConstant = reflect(AntiConstant)
class Rotate(Strategy):
""" A strategy that rotates through the possible moves. """
def __init__(self, first=ROCK, retrograde=False):
super().__init__()
self.first = first
self.retrograde = retrograde
def predict(self):
current = self.turn % 3
if current > 0 and self.retrograde:
current = 3 - current
shifted = (current + self.first) % 3
return beaten(shifted)
class AntiRotate(Strategy):
""" Assume that the opponent cycles through the possible moves. """
def __init__(self):
super().__init__()
self.tally = blank_tally()
self.previous = None
def observe(self, theirs, ours):
if self.previous is not None:
offset = outcome(theirs, self.previous)
self.tally[offset] += 1
self.previous = theirs
def predict(self):
previous = self.previous
if previous is None:
return self.arbitrary()
offset = most_frequent(self.tally)
return (previous + offset) % 3
ReflectiveAntiRotate = reflect(AntiRotate)
class Reactive(Strategy):
""" Respond to the opponent's previous move by a fixed offset. """
def __init__(self, initial=ROCK, offset=same):
super().__init__()
self.previous = offset(offset(initial))
self.offset = offset
def observe(self, theirs, ours):
self.previous = theirs
def predict(self):
intended_move = self.offset(self.previous)
return beaten(intended_move)
class AntiReactive(AntiRotate):
""" Assume that the opponent responds to our moves by a fixed offset. """
def observe(self, theirs, ours):
if self.previous:
offset = outcome(theirs, self.previous)
self.tally[offset] += 1
# A subtle but crucial difference with AntiRotate
self.previous = ours
ReflectiveAntiReactive = reflect(AntiReactive)
class Human(Strategy):
""" Represent a human player by asking for input. """
def observe(self, theirs, ours):
self.previous = theirs
def predict(self):
if hasattr(self, 'previous'):
print('Opponent\'s move:', REPRESENTATION[self.previous])
move = INTERPRETATION[input('Your move: ').strip().lower()[0]]
return beaten(move)
class MarkovChain(Strategy):
"""
Base the move on a pattern frequency analysis of the opponent.
Defeats all of the above strategies.
"""
def __init__(self, stride=4):
super().__init__()
self.stride = stride
self.window = ()
self.tally = {}
def observe(self, theirs, ours):
if self.turn >= self.stride:
tally = self.tally.get(self.window, blank_tally())
tally[theirs] += 1
self.tally[self.window] = tally
self.window = (*self.window[1:], theirs)
else:
self.window = (*self.window, theirs)
def predict(self):
tally = self.tally.get(self.window)
if tally is None:
return self.arbitrary()
return most_frequent(tally)
ReflectiveMarkovChain = reflect(MarkovChain)
class JplusStrategy(Strategy):
""" Counter all of the above strategies. """
def __init__(self):
super().__init__()
self.candidates = [
MarkovChain(),
ReflectiveMarkovChain(),
]
def __call__(self, round, their, our):
# Update this strategy's accuracy, ignoring the prediction.
super().__call__(round, their, our)
# Defer the actual move to the best performing candidate.
forward = methodcaller('weighted_call', round, their, our)
return max(map(forward, self.candidates), key=itemgetter(0))[1]
def predict(self):
# This method needs to be defined, but we never actually use the value.
return ROCK
def play_match(player1, player2):
""" Run a match between two strategy instances. """
moves1, moves2 = [], []
for round in range(5000):
try:
move1 = player1(round, moves2, moves1)
move2 = player2(round, moves1, moves2)
moves1.append(move1)
moves2.append(move2)
except KeyboardInterrupt:
break
return moves1, moves2
def print_match(player1, player2, moves1, moves2):
""" Show the results of a match to stdout. """
print('Player 1 accuracy:', player1.accuracy())
print('Player 2 accuracy:', player2.accuracy())
print()
for round, (move1, move2) in enumerate(zip(moves1, moves2)):
if round == 60:
break
show1 = REPRESENTATION[move1]
show2 = REPRESENTATION[move2]
print('{}: {}, {}'.format(round, show1, show2))
# Run the module to try the strategy against yourself!
if __name__ == '__main__':
opponent = Human()
self = JplusStrategy()
moves = play_match(opponent, self)
print_match(opponent, self, *moves)
What would you prefer?commodorejohn wrote: ↑Fri Mar 03, 2023 1:31 am I'm interested. Is this maze generation, or maze solving?
If we could somehow make the game less time-consuming, would that help you?
I couldn't be bothered to put a sorting algorithm together for the first round, I'm not sure you could make it quicker without losing the... challenge aspect of the challenge.
i'm not quite sure how you would evaluate a maze generation algorithm, what you would give badges for. maybe number of dead ends?If I'm going to host and we're doing mazes, I want at least three other participants. Also, it is likely that multiple weeks will pass before I have programmed enough to be able to give a clear description of the challenge.