ПОЗДРАВЛЯЕМ @kuroineko за лучшую запись и выигрыш в размере 200 от @TheBestOne (отличное спортивное мастерство!).
Напишите программу, чтобы раскрасить как можно большую часть изображения до того, как это сделают оппозиционные программы.
Правила вкратце
- Ваша программа получит изображение, ваш цвет и целое число N.
- Каждый ход вам посылают обновления пикселей другими программами и запрашивают ваши N обновлений.
- Вы можете обновить любой белый пиксель, который находится рядом с пикселем вашего цвета.
- Программа, которая добавила наибольшее количество пикселей, выигрывает.
Правила в деталях
Ваша программа получит имя файла PNG-изображения, домашний цвет и число N. Число N - это максимальное количество пикселей, которые ваша программа может раскрашивать каждый ход.
Пример: MyProg arena.png (255,0,0) 30
Входное изображение будет прямоугольником со сторонами от 20 до 1000 пикселей. Он будет состоять из черного, белого и цветного пикселей. Ваша программа может выбрать последовательность белых пикселей для окрашивания как свою собственную, при условии, что каждый новый пиксель должен иметь хотя бы один из четырех соседних пикселей вашего собственного цвета. Изображение изначально будет иметь как минимум один пиксель вашего цвета. Также могут быть пиксели цветов, которым не назначена ни одна программа. Альфа-канал не используется.
Ваша цель состоит в том, чтобы блокировать своих оппонентов и записать свой цвет в максимально возможное количество пикселей.
Каждый ход ваша программа будет принимать 1 или более строк сообщений в STDIN и записывать строку, состоящую из координат пикселей в STDOUT. Не забудьте назначить STDOUT небуферизованным или очищать буфер STDOUT каждый ход.
Порядок игроков, вызываемых каждый ход, будет назначен случайным образом. Это означает, что у противника (или вашей программы) может быть 2 хода подряд.
Ваша программа будет отправлять colour (N,N,N) chose X,Y X,Y ... X,Y
информационные сообщения, описывающие пиксели, заполненные программами плеера. Если игрок не делает ходов или не имеет действительных ходов, вам не будет отправлено сообщение о ходах этого игрока. Вашей программе также будет отправлено сообщение о ваших собственных принятых ходах (если вы указали хотя бы один действительный ход). Пиксель 0,0 находится в верхнем левом углу изображения.
При получении pick pixels
ваша программа будет выводить X,Y X,Y ... X,Y
до N пикселей (допускается пустая строка, состоящая только из '\ n'). Пиксели должны быть в порядке построения. Если пиксель недействителен, он будет проигнорирован и не будет в отчете игрокам. Ваша программа имеет 2 секунды для инициализации после запуска, но только 0,1 секунды, чтобы отвечать ответом каждый ход, иначе она пропустит этот ход. Обновление пикселей, отправленное через 0,1 секунды, запишет ошибку. После 5 ошибок ваша программа будет приостановлена и не будет отправляться обновления или pick pixels
запросы.
Когда программа-судья получает выбор пустого или недействительного пикселя от каждой неподдерживаемой программы проигрывателя, изображение будет считаться завершенным, и программам будет отправлено сообщение «Выход». Программы должны завершаться после получения «выхода».
счет
Судья наберет очки после того, как изображение будет завершено. Ваша оценка будет вашим количеством обновленных пикселей, деленным на среднее количество пикселей, полученных за этот раунд, выраженное в процентах.
Количество пикселей, добавленных к изображению вашим плеером, равно A. Общее количество пикселей, добавленных всеми игроками P, равно T.
avg = T/P
score = 100*A/avg
Отправка баллов
Ссылка оппонента "Blob" дается. Для каждого ответа назовите своего бота именем, языком и вашим счетом (в среднем от 1 до 4) по отношению к оппоненту. Картинка или анимация одного из ваших сражений тоже подойдут. Победителем становится программа с наибольшим счетом по отношению к контрольному боту.
Если «Блоб» окажется слишком легко победить, я могу добавить второй раунд с более сильным оппонентом.
Вы также можете поэкспериментировать с 4 или более программами игрока. Вы также можете проверить своего бота против других ботов, опубликованных в качестве ответов.
Судья
Для программы судьи требуется общая библиотека изображений Python (PIL), и ее легко установить из диспетчера пакетов ОС в Linux. У меня есть отчет о том, что PIL не работает с 64-битным Python в Windows 7, поэтому, пожалуйста, проверьте, будет ли PIL работать для вас, прежде чем начинать эту задачу (обновлено 2015-01-29).
#!/usr/bin/env python
# Judge Program for Image Battle challenge on PPCG.
# Runs on Python 2.7 on Ubuntu Linux. May need edits for other platforms.
# V1.0 First release.
# V1.1 Added Java support
# V1.2 Added Java inner class support
# usage: judge cfg.py
import sys, re, random, os, shutil, subprocess, datetime, time, signal
from PIL import Image
ORTH = ((-1,0), (1,0), (0,-1), (0,1))
def place(loc, colour):
# if valid, place colour at loc and return True, else False
if pix[loc] == (255,255,255):
plist = [(loc[0]+dx, loc[1]+dy) for dx,dy in ORTH]
if any(pix[p]==colour for p in plist if 0<=p[0]<W and 0<=p[1]<H):
pix[loc] = colour
return True
return False
def updateimage(image, msg, bot):
if not re.match(r'(\s*\d+,\d+)*\s*', msg):
return []
plist = [tuple(int(v) for v in pr.split(',')) for pr in msg.split()]
plist = plist[:PIXELBATCH]
return [p for p in plist if place(p, bot.colour)]
class Bot:
botlist = []
def __init__(self, name, interpreter=None, colour=None):
self.prog = name
self.botlist.append(self)
callarg = re.sub(r'\.class$', '', name) # Java fix
self.call = [interpreter, callarg] if interpreter else [callarg]
self.colour = colour
self.colstr = str(colour).replace(' ', '')
self.faults = 0
self.env = 'env%u' % self.botlist.index(self)
try: os.mkdir(self.env)
except: pass
if name.endswith('.class'): # Java inner class fix
rootname = re.sub(r'\.class$', '', name)
for fn in os.listdir('.'):
if fn.startswith(rootname) and fn.endswith('.class'):
shutil.copy(fn, self.env)
else:
shutil.copy(self.prog, self.env)
shutil.copy(imagename, self.env)
os.chdir(self.env)
args = self.call + [imagename, self.colstr, `PIXELBATCH`]
self.proc = subprocess.Popen(args, stdin=subprocess.PIPE,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
os.chdir('..')
def send(self, msg):
if self.faults < FAULTLIMIT:
self.proc.stdin.write(msg + '\n')
self.proc.stdin.flush()
def read(self, timelimit):
if self.faults < FAULTLIMIT:
start = time.time()
inline = self.proc.stdout.readline()
if time.time() - start > timelimit:
self.faults += 1
inline = ''
return inline.strip()
def exit(self):
self.send('exit')
from cfg import *
for i, (prog, interp) in enumerate(botspec):
Bot(prog, interp, colourspec[i])
image = Image.open(imagename)
pix = image.load()
W,H = image.size
time.sleep(INITTIME)
total = 0
for turn in range(1, MAXTURNS+1):
random.shuffle(Bot.botlist)
nullbots = 0
for bot in Bot.botlist:
bot.send('pick pixels')
inmsg = bot.read(TIMELIMIT)
newpixels = updateimage(image, inmsg, bot)
total += len(newpixels)
if newpixels:
pixtext = ' '.join('%u,%u'%p for p in newpixels)
msg = 'colour %s chose %s' % (bot.colstr, pixtext)
for msgbot in Bot.botlist:
msgbot.send(msg)
else:
nullbots += 1
if nullbots == len(Bot.botlist):
break
if turn % 100 == 0: print 'Turn %s done %s pixels' % (turn, total)
for msgbot in Bot.botlist:
msgbot.exit()
counts = dict((c,f) for f,c in image.getcolors(W*H))
avg = 1.0 * sum(counts.values()) / len(Bot.botlist)
for bot in Bot.botlist:
score = 100 * counts[bot.colour] / avg
print 'Bot %s with colour %s scored %s' % (bot.prog, bot.colour, score)
image.save(BATTLE+'.png')
Пример конфигурации - cfg.py
BATTLE = 'Green Blob vs Red Blob'
MAXTURNS = 20000
PIXELBATCH = 10
INITTIME = 2.0
TIMELIMIT = 0.1
FAULTLIMIT = 5
imagename = 'arena1.png'
colourspec = (0,255,0), (255,0,0)
botspec = [
('blob.py', 'python'),
('blob.py', 'python'),
]
Blob - ссылочный оппонент
# Blob v1.0 - A reference opponent for the Image Battle challenge on PPCG.
import sys, os
from PIL import Image
image = Image.open(sys.argv[1])
pix = image.load()
W,H = image.size
mycolour = eval(sys.argv[2])
pixbatch = int(sys.argv[3])
ORTH = ((-1,0), (1,0), (0,-1), (0,1))
def canchoose(loc, colour):
if pix[loc] == (255,255,255):
plist = [(loc[0]+dx, loc[1]+dy) for dx,dy in ORTH]
if any(pix[p]==colour for p in plist if 0<=p[0]<W and 0<=p[1]<H):
return True
return False
def near(loc):
plist = [(loc[0]+dx, loc[1]+dy) for dx,dy in ORTH]
pboard = [p for p in plist if 0<=p[0]<W and 0<=p[1]<H]
return [p for p in pboard if pix[p] == (255,255,255)]
def updateimage(image, msg):
ctext, colourtext, chose, points = msg.split(None, 3)
colour = eval(colourtext)
plist = [tuple(int(v) for v in pr.split(',')) for pr in points.split()]
for p in plist:
pix[p] = colour
skin.discard(p)
if colour == mycolour:
for np in near(p):
skin.add(np)
board = [(x,y) for x in range(W) for y in range(H)]
skin = set(p for p in board if canchoose(p, mycolour))
while 1:
msg = sys.stdin.readline()
if msg.startswith('colour'):
updateimage(image, msg.strip())
if msg.startswith('pick'):
plen = min(pixbatch, len(skin))
moves = [skin.pop() for i in range(plen)]
movetext = ' '.join('%u,%u'%p for p in moves)
sys.stdout.write(movetext + '\n')
sys.stdout.flush()
if msg.startswith('exit'):
break
image.save('blob.png')
Арена 1
Арена 2
Арена 3
Арена 4
Пример битвы - Blob против Blob
Эта битва имела предсказуемый результат:
Bot blob.py with colour (255, 0, 0) scored 89.2883333333
Bot blob.py with colour (0, 255, 0) scored 89.365
источник
Ответы:
ColorFighter - C ++ - ест пару глотателей на завтрак
РЕДАКТИРОВАТЬ
Боже, я ненавижу змей (просто притворяйся, что они пауки, Инди)
На самом деле я люблю Python. Хотелось бы, чтобы я был менее ленивым мальчиком и начал учить его правильно, вот и все.
После всего этого, мне пришлось бороться с 64-битной версией этой змеи, чтобы заставить судью работать. Чтобы PIL работал с 64-битной версией Python под Win7, требуется больше терпения, чем я готов был посвятить этому испытанию, поэтому в итоге я (болезненно) переключился на версию Win32.
Кроме того, судья имеет тенденцию сильно падать, когда бот слишком медленно реагирует.
Будучи не разбирающимся в Python, я не исправил это, но это связано с чтением пустого ответа после тайм-аута на stdin.
Незначительным улучшением было бы поместить вывод stderr в файл для каждого бота. Это облегчило бы отслеживание для посмертной отладки.
За исключением этих мелких проблем, я нашел Судью очень простым и приятным в использовании.
Престижность для еще одного изобретательного и веселого испытания.
Код
Сборка исполняемого файла
Я использовал LODEpng.cpp и LODEpng.h для чтения изображений PNG.
О самом простом способе, которым я научил этот отсталый язык C ++, как читать картинку, не создавая полдюжины библиотек.
Просто скомпилируйте и свяжите LODEpng.cpp вместе с главной и Бобом, вашим дядей.
Я скомпилировал с MSVC2013, но так как я использовал только несколько базовых контейнеров STL (deque и векторы), он может работать с gcc (если вам повезет).
Если это не так, я мог бы попробовать сборку MinGW, но, честно говоря, я устал от проблем с переносимостью C ++.
В свое время я много занимался переносом C / C ++ (на экзотических компиляторах для различных 8–32-битных процессоров, а также на SunOS, Windows от 3.11 до Vista и Linux от младенчества до Ubuntu Cooing Zebra или чего-то еще, так что я думаю, У меня есть довольно хорошее представление о том, что означает переносимость), но в то время не требовалось запоминать (или обнаруживать) бесчисленные расхождения между интерпретациями GNU и Microsoft загадочных и раздутых спецификаций монстра STL.
Результаты против Swallower
Как это работает
По сути, это простой путь заливки.
Граница цвета игрока (то есть пикселей, которые имеют по крайней мере одного белого соседа) используется в качестве начального числа для выполнения классического алгоритма затопления расстояний.
Когда точка достигает окрестности вражеского цвета, вычисляется обратный путь, чтобы создать ряд пикселей, движущихся в направлении ближайшего вражеского пятна.
Процесс повторяется до тех пор, пока не будет набрано достаточное количество точек для ответа желаемой длины.
Это повторение непристойно дорого, особенно когда сражаешься рядом с врагом.
Каждый раз, когда последовательность пикселей, ведущая от границы к вражескому пикселю, была найдена (и нам нужно больше точек, чтобы завершить ответ), заливка затопляется заново с начала, с новым путем, добавленным к границе. Это означает, что вы могли бы сделать 5 заливок или больше, чтобы получить ответ в 10 пикселей.
Если вражеские пиксели больше недоступны, выбираются произвольные соседи пограничных пикселей.
Алгоритм сводится к довольно неэффективному заполнению флудом, но это происходит только после того, как будет решен исход игры (то есть больше нет нейтральной территории для борьбы).
Я оптимизировал его так, чтобы судья не тратил целую вечность, заполняя карту после того, как соревнование было рассмотрено. В своем текущем состоянии время исполнения пренебрежимо мало по сравнению с самим судьей.
Поскольку вражеские цвета не известны в начале, исходное изображение арены сохраняется в хранилище, чтобы скопировать начальные области врага, когда он делает свой первый ход.
Если код воспроизводится первым, он просто заполняет несколько произвольных пикселей.
Это делает алгоритм способным бороться с произвольным числом противников и даже, возможно, с новыми противниками, прибывающими в произвольный момент времени, или с цветами, появляющимися без начальной области (хотя это абсолютно не имеет практического применения).
Обработка врага на основе цвета за цвет также позволила бы двум экземплярам взаимодействовать с ботом (используя пиксельные координаты для передачи секретного знака распознавания).
Звучит весело, я, наверное, попробую :).
Интенсивный вычислительный путь выполняется, как только появляются новые данные (после уведомления о перемещении), и некоторые оптимизации (обновление границы) выполняются сразу после получения ответа (чтобы сделать как можно больше вычислений во время поворотов других ботов) ).
Здесь снова могут быть способы сделать более тонкие вещи, если бы было более одного противника (например, прервать вычисление, если новые данные стали доступны), но, во всяком случае, я не вижу, где необходима многозадачность, пока алгоритм способен работать на полную нагрузку.
Проблемы с производительностью
Все это не может работать без быстрого доступа к данным (и большей вычислительной мощности, чем у всей программы Appolo, т. Е. Вашего обычного ПК, когда вы используете больше, чем публикуете несколько твитов).
Скорость сильно зависит от компилятора. Обычно GNU опережает Microsoft на 30% (это магическое число, которое я заметил при трех других проблемах с кодом, связанным с маршрутизацией), но этот пробег может меняться, конечно.
Код в его текущем состоянии с трудом справляется с трудностями на арене 4. Перфметр Windows сообщает о 4–7% -ной загрузке ЦП, поэтому он должен быть в состоянии справиться с картой 1000x1000 в пределах времени отклика 100 мс.
В основе каждого алгоритма маршрутизации лежит FIFO (возможно, проритизированный, но не в этом случае), который, в свою очередь, требует быстрого выделения элементов.
Так как ОП обязательно установил ограничение на размер арены, я немного посчитал и увидел, что фиксированные структуры данных размером до максимума (т. Е. 1.000.000 пикселей) не будут потреблять более пары десятков мегабайт, которые ваш средний компьютер ест на завтрак.
Действительно, под Win7 и скомпилированный с MSVC 2013, код потребляет около 14 МБ на арене 4, в то время как два потока Swallower используют более 20 МБ.
Я начал с контейнеров STL для упрощения создания прототипов, но STL сделал код еще менее читаемым, поскольку у меня не было желания создавать класс для инкапсуляции каждого бита данных, чтобы скрыть запутывание (связано ли это с моими собственными возможностями справиться с STL оставлено на усмотрение читателя).
Несмотря на это, результат был настолько ужасно медленным, что сначала я думал, что по ошибке строю отладочную версию.
Я считаю, что это отчасти связано с невероятно плохой реализацией Microsoft STL (где, например, векторы и наборы битов выполняют связанные проверки или другие криптические операции над оператором [], в прямом нарушении спецификации), а также частично из-за конструкции STL сам.
Я мог бы справиться с жестокими проблемами синтаксиса и переносимости (то есть Microsoft против GNU), если бы производительность была там, но это, конечно, не так.
Например,
deque
изначально медленный, потому что он тасует много бухгалтерских данных в ожидании случая, чтобы сделать его супер-умное изменение размера, о котором мне наплевать.Конечно, я мог бы реализовать собственный распределитель и другие биты пользовательских шаблонов, но один только собственный распределитель стоит несколько сотен строк кода и лучшую часть дня, чтобы проверить, что с дюжиной интерфейсов, которые он должен реализовать, в то время как Эквивалентная структура, созданная вручную, состоит из нуля строк кода (хотя и более опасно, но алгоритм не сработал бы, если бы я не знал - или, думаю, знал - что я делал в любом случае).
Таким образом, в конце концов я сохранил контейнеры STL в некритических частях кода и создал свой собственный брутальный распределитель и FIFO с двумя массивами 1970 года и тремя шортами без знака.
Глотать глотатель
Как подтвердил его автор, неустойчивые паттерны «ласточки» вызваны задержкой между уведомлениями о перемещениях противника и обновлениями из потока пути.
Системный перфметр четко показывает траекторию потока, потребляющую 100% ЦП все время, и неровные узоры имеют тенденцию появляться, когда фокус сражения смещается в новую область. Это также вполне очевидно с анимацией.
Простая, но эффективная оптимизация
Посмотрев на грандиозные воздушные бои между Ласточкой и моим бойцом, я вспомнил старую поговорку из игры в Го: защищайся крупным планом, но атакуй издалека.
В этом есть мудрость. Если вы слишком стараетесь придерживаться своего противника, вы будете тратить драгоценные ходы, пытаясь заблокировать каждый возможный путь. Напротив, если вы останетесь на расстоянии всего одного пикселя, вы, скорее всего, избежите заполнения небольших пробелов, которые могут принести очень мало, и будете использовать свои действия для противодействия более важным угрозам.
Чтобы реализовать эту идею, я просто расширил ходы врага (отметив 4 соседа каждого хода как вражеский пиксель).
Это останавливает алгоритм прохождения пути на расстоянии одного пикселя от границы врага, позволяя моему бойцу обходить противника, не оказываясь втянутым в слишком много воздушных боев.
Вы можете увидеть улучшение
(хотя все прогоны не так успешны, вы можете заметить более плавные контуры):
источник
Глубина-первая Blob против Blob
Язык = Python (3.2)
Оценка =
111,475388276153,34210035Обновление: теперь используя пользовательский
Set
класс, чтобы получитьpop()
метод для создания своего рода сетки, которая радикально улучшает область, покрытую в начале, отрезая большие части изображения от врага. Примечание: для этого я использую сетку 12 x 12, которая из случайной выборки размеров сетки, по-видимому, дала наилучшие результаты для arena3 (той, которая получила худший результат перед обновлением), однако весьма вероятно, что более оптимальная Размер сетки существует для данного выбора арен.Я пошел на простую модификацию эталонного бота, чтобы он выбирал возможные точки, ограниченные как можно меньшим количеством точек собственного цвета. Улучшение может заключаться в том, чтобы оно также способствовало выбору возможных точек, ограниченных как можно большим количеством цветов врага.
dfblob.py:
Оригинальный судья был немного изменен для работы с Python 3.2 (и для добавления грубой логирующей функциональности к ботам + периодически сохраняйте изображение арены для создания анимации):
Результаты арены следуют. Бот dfblob получил красный цвет для всех арен.
Арена 1:
Арена 2:
Арена 3:
Арена 4:
источник
convert -delay 5 -loop 0 result*.png animated.gif
хотя некоторые из картинок нужно было позже вручную вырезать, чтобы загрузить сюдаSwallower
Язык = Java
Оценка =
+162,3289512601408075+169,4020975612382575Ищет врагов и окружает.
Возможно, вам придется продлить срок. Может быть улучшен совсем немного. Иногда печатает недопустимые пиксели.Обновление: Окружает намного быстрее. Использует другой поток для обновления приоритетов. Всегда возвращается в течение 0,1 секунды. Оценка должна быть невозможной без увеличения
MAX_TURNS
.Как это работает:
Этот бот поддерживает приоритетную очередь пикселей, которые он может добавить. Приоритет вражеского пикселя равен 0. Приоритет пустого пикселя на 1 больше, чем самый низкий приоритет вокруг него. Все остальные пиксели имеют приоритет Integer.MAX_VALUE. Тема обновления постоянно обновляет приоритеты пикселей. Каждый ход наименьшие N пикселей выталкиваются из очереди приоритетов.
Грин Блоб против Красного Глотателя
Оценка Blob = 1.680553372583887225
Счет ласточкина = 169.4020975612382575
Арена 1:
Арена 2:
Арена 3:
Арена 4:
Зеленый глотатель против Красной капли
Оценка Blob = 1.6852943642218457375
Счет ласточкина = 169.3923095387498625
Арена 1:
Арена 2:
Арена 3:
Арена 4:
Red Swallower против Green Depth Первая капля
Счет ласточкина = 157.0749775233111925
Глубина Оценка первого BLOB-объекта = 18.192783547939744
Арена 1:
Арена 2:
Арена 3:
Арена 4:
Green Swallower против Red Depth Первая капля
Счет ласточкина = 154.3368355651281075
Глубина Оценка первого BLOB-объекта = 18,84463249420435425
Арена 1:
Арена 2:
Арена 3:
Арена 4:
Зеленая капля против красной глубины Первая капля против синей ласточки:
Оценка Blob = 6,347962032393275525
Глубина Оценка первого BLOB-объекта = 27,34842554331698275
Оценка ласточкина = 227.720728953415375
Арена 1:
Арена 2:
Арена 3:
Арена 4:
Вот судья Сэма Йонну с несколькими изменениями, чтобы вы указали файлы и команду отдельно:
Пример cfg:
Примечание. Любой, кому удастся проглотить глотатель, получает 100 репутации. Пожалуйста, напишите в комментариях ниже, если вам это удастся.
источник
Случайный, Язык = Java, Оценка = 0,43012126100275
Эта программа случайным образом размещает пиксели на экране. Некоторые (если не все) пиксели не будут действительными. Кстати, должно быть трудно сделать более быструю программу, чем эта.
Арена 1:
Арена 2:
Арена 3:
Арена 4:
источник