happybot/fractran.py
2021-06-19 16:50:03 -04:00

144 lines
4.1 KiB
Python
Executable file

#!/usr/bin/env python3
from subprocess import Popen, PIPE
import re
def cmd(args):
proc = Popen(args, stdout=PIPE)
while True:
line = proc.stdout.readline()
if line:
try:
yield str(line[:-1], 'utf-8', 'ignore')
except:
pass
else:
break
def irc(chan):
global trigger
fdir = '/home/zgrep/offtopiabday/irc.freenode.net/' + chan
fin = fdir + '/in'
fout = fdir + '/out'
for line in cmd(['tail', '-n', '0', '-f', fout]):
date, time, nick, line = line.split(' ', 3)
print(line)
m = re.match(r'(?i)(?:happy|hate)bot[:,]\s+([0-9]+)\s+(fractrand?)\s+([0-9]+/[0-9]+(?:,?\s+[0-9]+/[0-9]+)*)$', line)
if m:
n, f, fs = m.group(1), m.group(2), m.group(3)
print('Matched!', n, f, fs)
trigger = True
r = doit(f, n, fs)
trigger = False
r = '\u200b' + r
print('Result!', r)
with open(fin, 'w') as fh:
fh.write(r + '\n')
from fractions import Fraction
from random import choice
import signal
trigger = False
class TimeoutException(Exception): pass
def signal_handler(signum, frame):
global trigger
if trigger:
raise TimeoutException("Timed out!")
def fractran(n, fracs):
vals = [n]
signal.alarm(1)
try:
while True:
for frac in fracs:
n2 = n*frac
if n2.denominator == 1:
n = n2.numerator
if n in vals:
i = vals.index(n)
return 'Infinite loop: ..., ' + ', '.join(map(str, vals[i:]))
vals.append(n)
break
else:
if len(vals) > 12:
beg = ', '.join(map(str, vals[:2]))
end = ', '.join(map(str, vals[-10:]))
return f'{beg}, ..., {end}'
else:
return ', '.join(map(str, vals))
except TimeoutException:
if len(vals) > 12:
beg = ', '.join(map(str, vals[:10]))
end = ', '.join(map(str, vals[-2:]))
return f'Timed out: {beg}, ..., {end}, ...'
else:
return 'Timed out: ', + ', '.join(map(str, vals))
def random_unordered_fractran(n, fracs):
vals = [n]
signal.alarm(1)
try:
while True:
ns = [ n * frac for frac in fracs ]
ns = [ m for m in ns if m.denominator == 1 ]
if not ns:
if len(vals) > 12:
beg = ', '.join(map(str, vals[:2]))
end = ', '.join(map(str, vals[-10:]))
return f'{beg}, ..., {end}'
else:
return ', '.join(map(str, vals))
n = choice(ns)
vals.append(n)
except TimeoutException:
if len(vals) > 12:
beg = ', '.join(map(str, vals[:10]))
end = ', '.join(map(str, vals[-2:]))
return f'Timed out: {beg}, ..., {end}, ...'
else:
return 'Timed out: ', + ', '.join(map(str, vals))
def main():
from sys import argv
if len(argv) == 2:
# IRC mode.
irc(argv[1])
elif len(argv) == 4:
# cli mode.
print(doit(argv[1], argv[2], argv[3]))
else:
print('Wrong number of arguments.')
exit(1)
def doit(func, inp, args):
n, fracs = None, None
try:
n = int(inp)
except:
return 'Input is not an integer?'
try:
fracs = [Fraction(*map(int, x.split('/', 1))) for x in args.replace(',', '').split()]
except:
return 'Could not parse fraction list.'
if func == 'fractran':
func = fractran
elif func == 'fractrand':
func = random_unordered_fractran
else:
return f'Invalid function: {func}'
signal.signal(signal.SIGALRM, signal_handler)
try:
return func(n, fracs)
except TimeoutException:
return 'Timed out.'
except Exception as e:
return 'Some error occurred: ' + str (e)
return 'Something went wrong.'
if __name__ == '__main__':
main()