コードランナー
そのため、面白くするために、投稿されたすべての回答からコードを自動的にダウンロードし、必要に応じてコンパイルし、ルールに従ってすべてのソリューションを実行するスクリプトを作成しました。この方法で、人々は彼らがどのように行っているかをチェックできます。このスクリプトをrun_all.py(BeautifulSoupが必要)に保存してから、次のようにします。
usage:
To get the latest code: 'python run_all.py get'
To run the submissions: 'python run_all.py run <optional num_runs>'
いくつかのこと:
- さらに多くの言語のサポートを追加する場合、または一部の言語のサポートを削除する場合は、を参照してください
def submission_type(lang)
。
- コンパイルが必要な言語であっても、スクリプトの拡張はかなり簡単です(参考文献を参照
CPPSubmission
)。言語タイプはメタコードタグから< !-- language: lang-java -- >
取得されるため、コードを実行する場合は必ず追加してください(<>の前後の余分なスペースを削除します)。
更新:言語が定義されていない場合、言語を試行および検出するための非常に基本的な推論がいくつかあります。
- コードの実行にまったく失敗した場合、または割り当てられた時間内に終了しない場合、コードは
blacklist.text
将来のトライアルに自動的に追加され、削除されます。コードを修正する場合は、ブラックリストからエントリを削除して、再実行してくださいget
。
現在サポートされている言語:
submission_types = {
'lang-ruby': RubySubmission,
'lang-python': PythonSubmission,
'lang-py': PythonSubmission,
'lang-java': JavaSubmission,
'lang-Java': JavaSubmission,
'lang-javascript': NodeSubmission,
'lang-cpp': CPPSubmission,
'lang-c': CSubmission,
'lang-lua': LuaSubmission,
'lang-r': RSubmission,
'lang-fortran': FortranSubmission,
'lang-bash': BashSubmission
}
難しい話は抜きにして:
import urllib2
import hashlib
import os
import re
import subprocess
import shutil
import time
import multiprocessing
import tempfile
import sys
from bs4 import BeautifulSoup
__run_java__ = """
public class Run {
public static void main(String[] args) {
String input = "";
Human h = new __REPLACE_ME__();
if(args.length == 1)
input = args[0];
try {
System.out.println(h.takeSides(input));
}
catch(Exception e) {
}
}
}
"""
__human_java__ = """
public abstract class Human {
public abstract String takeSides(String history) throws Exception;
}
"""
class Submission():
def __init__(self, name, code):
self.name = name
self.code = code
def submissions_dir(self):
return 'submission'
def base_name(self):
return 'run'
def submission_path(self):
return os.path.join(self.submissions_dir(), self.name)
def extension(self):
return ""
def save_submission(self):
self.save_code()
def full_command(self, input):
return []
def full_path(self):
file_name = "%s.%s" % (self.base_name(), self.extension())
full_path = os.path.join(self.submission_path(), file_name)
return full_path
def save_code(self):
if not os.path.exists(self.submission_path()):
os.makedirs(self.submission_path())
with open(self.full_path(), 'w') as f:
f.write(self.code)
def write_err(self, err):
with open(self.error_log(), 'w') as f:
f.write(err)
def error_log(self):
return os.path.join(self.submission_path(), 'error.txt')
def run_submission(self, input):
command = self.full_command()
if input is not None:
command.append(input)
try:
output,err,exit_code = run(command,timeout=1)
if len(err) > 0:
self.write_err(err)
return output
except Exception as e:
self.write_err(str(e))
return ""
class CPPSubmission(Submission):
def bin_path(self):
return os.path.join(self.submission_path(), self.base_name())
def save_submission(self):
self.save_code()
compile_cmd = ['g++', '-O3', '-std=c++0x', '-o', self.bin_path(), self.full_path()]
errout = open(self.error_log(), 'w')
subprocess.call(compile_cmd, stdout=errout, stderr=subprocess.STDOUT)
def extension(self):
return 'cpp'
def full_command(self):
return [self.bin_path()]
class CSubmission(Submission):
def bin_path(self):
return os.path.join(self.submission_path(), self.base_name())
def save_submission(self):
self.save_code()
compile_cmd = ['gcc', '-o', self.bin_path(), self.full_path()]
errout = open(self.error_log(), 'w')
subprocess.call(compile_cmd, stdout=errout, stderr=subprocess.STDOUT)
def extension(self):
return 'c'
def full_command(self):
return [self.bin_path()]
class FortranSubmission(Submission):
def bin_path(self):
return os.path.join(self.submission_path(), self.base_name())
def save_submission(self):
self.save_code()
compile_cmd = ['gfortran', '-fno-range-check', '-o', self.bin_path(), self.full_path()]
errout = open(self.error_log(), 'w')
subprocess.call(compile_cmd, stdout=errout, stderr=subprocess.STDOUT)
def extension(self):
return 'f90'
def full_command(self):
return [self.bin_path()]
class JavaSubmission(Submission):
def base_name(self):
class_name = re.search(r'class (\w+) extends', self.code)
file_name = class_name.group(1)
return file_name
def human_base_name(self):
return 'Human'
def run_base_name(self):
return 'Run'
def full_name(self, base_name):
return '%s.%s' % (base_name, self.extension())
def human_path(self):
return os.path.join(self.submission_path(), self.full_name(self.human_base_name()))
def run_path(self):
return os.path.join(self.submission_path(), self.full_name(self.run_base_name()))
def replace_in_file(self, file_name, str_orig, str_new):
old_data = open(file_name).read()
new_data = old_data.replace(str_orig, str_new)
with open(file_name, 'w') as f:
f.write(new_data)
def write_code_to_file(self, code_str, file_name):
with open(file_name, 'w') as f:
f.write(code_str)
def save_submission(self):
self.save_code()
self.write_code_to_file(__human_java__, self.human_path())
self.write_code_to_file(__run_java__, self.run_path())
self.replace_in_file(self.run_path(), '__REPLACE_ME__', self.base_name())
self.replace_in_file(self.full_path(), 'package Humans;', '')
compile_cmd = ['javac', '-cp', self.submission_path(), self.run_path()]
errout = open(self.error_log(), 'w')
subprocess.call(compile_cmd, stdout=errout, stderr=subprocess.STDOUT)
def extension(self):
return 'java'
def full_command(self):
return ['java', '-cp', self.submission_path(), self.run_base_name()]
class PythonSubmission(Submission):
def full_command(self):
return ['python', self.full_path()]
def extension(self):
return 'py'
class RubySubmission(Submission):
def full_command(self):
return ['ruby', self.full_path()]
def extension(self):
return 'rb'
class NodeSubmission(Submission):
def full_command(self):
return ['node', self.full_path()]
def extension(self):
return 'js'
class LuaSubmission(Submission):
def full_command(self):
return ['lua', self.full_path()]
def extension(self):
return 'lua'
class RSubmission(Submission):
def full_command(self):
return ['Rscript', self.full_path()]
def extension(self):
return 'R'
class BashSubmission(Submission):
def full_command(self):
return [self.full_path()]
def extension(self):
return '.sh'
class Scraper():
def download_page(self, url, use_cache = True, force_cache_update = False):
file_name = hashlib.sha1(url).hexdigest()
if not os.path.exists('cache'):
os.makedirs('cache')
full_path = os.path.join('cache', file_name)
file_exists = os.path.isfile(full_path)
if use_cache and file_exists and not force_cache_update:
html = open(full_path, 'r').read()
return html
opener = urllib2.build_opener()
opener.addheaders = [('User-agent', 'Mozilla/5.0')]
response = opener.open(url)
html = response.read()
if use_cache:
f = open(full_path, 'w')
f.write(html)
f.close()
return html
def parse_post(self, post):
name = post.find(text=lambda t: len(t.strip()) > 0)
pre = post.find('pre')
lang = pre.attrs['class'][0] if pre.has_attr('class') else None
code = pre.find('code').text
user = post.find(class_='user-details').find(text=True)
return {'name':name,'lang':lang,'code':code,'user':user}
def parse_posts(self, html):
soup = BeautifulSoup(html)
# Skip the first post
posts = soup.find_all(class_ = 'answercell')
return [self.parse_post(post) for post in posts]
def get_submissions(self, page = 1, force_cache_update = False):
url = "http://codegolf.stackexchange.com/questions/33137/good-versus-evil?page=%i&tab=votes#tab-top" % page
html = self.download_page(url, use_cache = True, force_cache_update = force_cache_update)
submissions = self.parse_posts(html)
return submissions
class Timeout(Exception):
pass
def run(command, timeout=10):
proc = subprocess.Popen(command, bufsize=0, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
poll_seconds = .250
deadline = time.time()+timeout
while time.time() < deadline and proc.poll() == None:
time.sleep(poll_seconds)
if proc.poll() == None:
if float(sys.version[:3]) >= 2.6:
proc.terminate()
raise Timeout()
stdout, stderr = proc.communicate()
return stdout, stderr, proc.returncode
def guess_lang(code):
if re.search(r'class .* extends Human', code):
return 'lang-java'
if re.search(r'import sys', code):
return 'lang-python'
if re.search(r'puts', code) and (re.search(r'ARGV', code) or re.search(r'\%w', code)):
return 'lang-ruby'
if re.search(r'console\.log', code):
return 'lang-javascript'
if re.search(r'program', code) and re.search(r'subroutine', code):
return 'lang-fortran'
if re.search(r'@echo off', code):
return 'lang-bash'
return None
def submission_type(lang, code):
submission_types = {
'lang-ruby': RubySubmission,
'lang-python': PythonSubmission,
'lang-py': PythonSubmission,
'lang-java': JavaSubmission,
'lang-Java': JavaSubmission,
'lang-javascript': NodeSubmission,
'lang-cpp': CPPSubmission,
'lang-c': CSubmission,
'lang-lua': LuaSubmission,
'lang-r': RSubmission,
'lang-fortran': FortranSubmission,
'lang-bash': BashSubmission
}
klass = submission_types.get(lang)
if klass is None:
lang = guess_lang(code)
klass = submission_types.get(lang)
return klass
def instantiate(submission):
lang = submission['lang']
code = submission['code']
name = submission['name']
klass = submission_type(lang, code)
if klass is not None:
instance = klass(name, code)
return instance
print "Entry %s invalid - lang not supported: %s" % (name, lang)
return None
def get_all_instances(force_update):
scraper = Scraper()
print 'Scraping Submissions..'
pages = [1,2,3]
submissions_by_page = [scraper.get_submissions(page=i, force_cache_update=force_update) for i in pages]
submissions = [item for sublist in submissions_by_page for item in sublist]
# Get instances
raw_instances = [instantiate(s) for s in submissions]
instances = [i for i in raw_instances if i]
print "Using %i/%i Submissions" % (len(instances), len(submissions))
return instances
def save_submissions(instances):
print 'Saving Submissions..'
for instance in instances:
instance.save_submission()
def init_game(save=True, force_update=False):
instances = get_all_instances(force_update)
if save:
save_submissions(instances)
return instances
def one_run(instances, input):
valid = {
'good': 1,
'evil': 0
}
disqualified = []
results = []
for instance in instances:
out = instance.run_submission(input)
res = out.strip().lower()
if res not in valid:
disqualified.append(instance)
else:
results.append(valid[res])
return (results, disqualified)
def get_winner(scores, instances):
max_value = max(scores)
max_index = scores.index(max_value)
instance = instances[max_index]
return (instance.name, max_value)
def update_scores(results, scores, minority_counts, minority_num):
for i in range(len(results)):
if results[i] == minority_num:
minority_counts[i] += 1
scores[i] += (minority_counts[i] - 1)
else:
minority_counts[i] = 0
scores[i] += 3
def try_run_game(instances, num_runs = 1000, blacklist = None):
current_input = None
minority_str = None
num_instances = len(instances)
scores = [0] * num_instances
minority_counts = [0] * num_instances
print "Running with %i instances..." % num_instances
for i in range(num_runs):
print "Round: %i - Last minority was %s" % (i, minority_str)
results, disqualified = one_run(instances, current_input)
if len(disqualified) > 0:
for instance in disqualified:
print "Removing %s!" % instance.name
instances.remove(instance)
if blacklist is not None:
with open(blacklist, 'a') as f:
f.write("%s\n" % instance.name)
return False
latest_result = "".join(map(str,results))
current_input = "%s,%s" % (current_input, latest_result)
minority_num = 1 if results.count(1) < results.count(0) else 0
minority_str = 'good' if minority_num == 1 else 'evil'
update_scores(results, scores, minority_counts, minority_num)
name, score = get_winner(scores, instances)
print "%s is currently winning with a score of %i" % (name, score)
print "The winner is %s with a score of %i!!!" % (name, score)
return True
def find_instance_by_name(instances, name):
for instance in instances:
if instance.name == name:
return instance
return None
def maybe_add_or_remove_baelish(instances, baelish):
num_instances = len(instances)
if num_instances % 2 == 0:
print 'There are %i instances.' % num_instances
try:
instances.remove(baelish)
print 'Baelish Removed!'
except:
instances.append(baelish)
print 'Baelish Added!'
def remove_blacklisted(blacklist, instances):
blacklisted = []
try:
blacklisted = open(blacklist).readlines()
except:
return
print 'Removing blacklisted entries...'
for name in blacklisted:
name = name.strip()
instance = find_instance_by_name(instances, name)
if instance is not None:
print 'Removing %s' % name
instances.remove(instance)
def run_game(instances, num_runs):
blacklist = 'blacklist.txt'
remove_blacklisted(blacklist, instances)
baelish = find_instance_by_name(instances, 'Petyr Baelish')
maybe_add_or_remove_baelish(instances, baelish)
while not try_run_game(instances, num_runs = num_runs, blacklist = blacklist):
print "Restarting!"
maybe_add_or_remove_baelish(instances, baelish)
print "Done!"
if __name__ == '__main__':
param = sys.argv[1] if len(sys.argv) >= 2 else None
if param == 'get':
instances = init_game(save=True, force_update=True)
elif param == 'run':
instances = init_game(save=False, force_update=False)
num_runs = 50
if len(sys.argv) == 3:
num_runs = int(sys.argv[2])
run_game(instances, num_runs)
else:
self_name = os.path.basename(__file__)
print "usage:"
print "To get the latest code: 'python %s get'" % self_name
print "To run the submissions: 'python %s run <optional num_runs>'" % self_name