Trận chiến sắc màu


33

HÃY THAM GIA để @kuroineko cho mục tốt nhất và giành được 200 tiền thưởng từ @TheBestOne (thể thao xuất sắc!).

Viết chương trình tô màu càng nhiều hình ảnh càng tốt trước khi các chương trình đối lập làm.

Quy tắc ngắn gọn

  • Chương trình của bạn sẽ được cung cấp một hình ảnh, màu sắc và số nguyên N.
  • Mỗi lượt bạn được gửi các bản cập nhật pixel bởi các chương trình khác và được yêu cầu cập nhật N của bạn.
  • Bạn có thể cập nhật bất kỳ pixel trắng nào bên cạnh một pixel màu của bạn.
  • Chương trình đã thêm nhiều pixel nhất sẽ thắng.

Quy tắc chi tiết

Chương trình của bạn sẽ được cung cấp tên tệp hình ảnh PNG, màu nhà và số N. Số N là số pixel tối đa mà chương trình của bạn có thể tô màu mỗi lượt.

Thí dụ: MyProg arena.png (255,0,0) 30

Hình ảnh đầu vào sẽ là một hình chữ nhật có các cạnh dài từ 20 đến 1000 pixel. Nó sẽ bao gồm các pixel đen, trắng và màu. Chương trình của bạn có thể chọn một chuỗi các pixel trắng để tô màu cho riêng bạn, với điều kiện mỗi pixel mới phải có ít nhất một trong bốn pixel lân cận có màu của riêng bạn. Hình ảnh ban đầu sẽ có ít nhất một pixel màu của bạn. Nó cũng có thể có các pixel màu mà không có chương trình nào được gán. Kênh alpha không được sử dụng.

Mục tiêu của bạn là chặn đối thủ và viết màu của bạn thành nhiều pixel nhất có thể.

Mỗi lượt, chương trình của bạn sẽ chấp nhận 1 hoặc nhiều dòng thông báo trên STDIN và viết một dòng bao gồm tọa độ pixel trên STDOUT. Hãy nhớ chỉ định STDOUT là không có bộ đệm hoặc xóa bộ đệm STDOUT mỗi lượt.

Thứ tự của người chơi được gọi mỗi lượt sẽ được chỉ định ngẫu nhiên. Điều này có nghĩa là một đối thủ (hoặc chương trình của bạn) có thể có 2 lượt liên tiếp.

Chương trình của bạn sẽ được gửi colour (N,N,N) chose X,Y X,Y ... X,Ytin nhắn thông tin mô tả các pixel được điền bởi các chương trình trình phát. Nếu một cầu thủ làm cho không có di chuyển, hoặc không có động thái hợp lệ, bạn sẽ không nhận được một tin nhắn về di chuyển của người chơi đó. Chương trình của bạn cũng sẽ nhận được một thông điệp về động thái được chấp nhận của riêng bạn (nếu bạn đã xác định ít nhất một động thái hợp lệ). Pixel 0,0 nằm ở góc trên cùng bên trái của hình ảnh.

Mở nhận pick pixels, bạn chương trình sẽ đầu ra X,Y X,Y ... X,Ylên đến N pixel (một chuỗi rỗng gồm chỉ là một '\ n' được phép). Các pixel phải theo thứ tự âm mưu. Nếu một pixel không hợp lệ, nó sẽ bị bỏ qua và không có trong báo cáo cho người chơi. Chương trình của bạn có 2 giây để khởi sau khi bắt đầu, nhưng chỉ có 0,1 giây để trả lời với một câu trả lời mỗi lượt hoặc nó sẽ bỏ lỡ lượt đó. Một bản cập nhật pixel được gửi sau 0,1 giây sẽ ghi lại lỗi. Sau 5 lỗi chương trình của bạn bị treo và sẽ không được gửi cập nhật hoặc pick pixelsyêu cầu.

Khi chương trình thẩm phán nhận được lựa chọn pixel trống hoặc không hợp lệ từ mọi chương trình trình phát không bị treo, hình ảnh sẽ được coi là hoàn thành và các chương trình sẽ được gửi thông báo "thoát". Các chương trình phải chấm dứt sau khi nhận được "thoát".

Chấm điểm

Thẩm phán sẽ ghi điểm sau khi hình ảnh hoàn thành. Điểm của bạn sẽ là số pixel của bạn được cập nhật chia cho số lần chụp pixel trung bình của vòng đó, được biểu thị bằng phần trăm.

Số lượng pixel thêm vào hình ảnh bằng cách nghe nhạc của bạn là A. tổng số pixel thêm bởi tất cả người chơi P là T. avg = T/P score = 100*A/avg

Đăng điểm

Một đối thủ tham chiếu "The Blob" được đưa ra. Đối với mỗi câu trả lời, tiêu đề bot của bạn với một tên, ngôn ngữ, và điểm số của bạn (trung bình của trường từ 1 đến 4) chống lại các đối thủ tham khảo. Một hình ảnh hoặc hoạt hình của một trong những trận chiến của bạn cũng sẽ tốt. Người chiến thắng là chương trình có số điểm cao nhất so với bot tham khảo.

Nếu The Blob tỏ ra quá dễ đánh bại, tôi có thể thêm vòng thứ hai với đối thủ tham chiếu mạnh hơn.

Bạn cũng có thể muốn thử nghiệm với 4 chương trình người chơi trở lên. Bạn cũng có thể kiểm tra bot của bạn chống lại các bot khác được đăng dưới dạng câu trả lời.

Thẩm phán

Chương trình thẩm phán yêu cầu Thư viện hình ảnh Python (PIL) phổ biến và phải dễ cài đặt từ trình quản lý gói hệ điều hành của bạn trên Linux. Tôi có một báo cáo rằng PIL không hoạt động với Python 64 bit trên Windows 7, vì vậy vui lòng kiểm tra xem PIL có hoạt động với bạn không trước khi bắt đầu thử thách này (cập nhật 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')

Cấu hình ví dụ - 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'),
    ]

The Blob - đối thủ tham chiếu

# 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')

Đấu trường 1

đấu trường1.png

Đấu trường 2

đấu trường2.png

Đấu trường 3

đấu trường3.png

Đấu trường 4

đấu trường4.png

Một trận chiến ví dụ - Blob vs Blob

Trận chiến này đã có một kết quả có thể dự đoán được:

Bot blob.py with colour (255, 0, 0) scored 89.2883333333
Bot blob.py with colour (0, 255, 0) scored 89.365

Trận chiến ví dụ


Bạn có chắc đây không phải là [vua đồi]?
Justin

Tôi nghĩ về điều đó. Các bot không chiến đấu trực tiếp với nhau. Họ chiến đấu với bot tham khảo. Điều đó có loại trừ KOTH không?
Logic Knight

Vâng, vì đây là, nó không phải là một KOTH, tôi đã hỏi liệu bạn có chắc chắn muốn chiến đấu với bot tham chiếu hơn là nhau không.
Justin

1
@TheBestOne, Đã thêm hỗ trợ Java. Chưa được kiểm tra với chương trình Java. Hãy cho tôi biết nếu nó không hoạt động.
Logic Knight

1
10 pixel được đặt theo thứ tự, vì vậy các pixel sau này có thể dựa vào các vị trí pixel trước đó. Họ có thể xây dựng trên nhau như bạn đề nghị.
Logic Knight

Câu trả lời:


17

ColorFighter - C ++ - ăn một vài con én cho bữa sáng

CHỈNH SỬA

  • làm sạch mã
  • thêm một tối ưu hóa đơn giản nhưng hiệu quả
  • đã thêm một số hình ảnh động GIF

Chúa tôi ghét rắn (chỉ giả vờ rằng chúng là nhện, Indy)

Thật ra tôi yêu Python. Tôi ước mình bớt đi một cậu bé lười biếng và bắt đầu học nó đúng cách, thế thôi.

Tất cả điều này đã được nói, tôi đã phải vật lộn với phiên bản 64 bit của con rắn này để khiến Thẩm phán hoạt động. Làm cho PIL hoạt động với phiên bản 64 bit của Python trong Win7 đòi hỏi sự kiên nhẫn hơn tôi sẵn sàng dành cho thử thách này, vì vậy cuối cùng tôi đã chuyển (đau đớn) sang phiên bản Win32.

Ngoài ra, Thẩm phán có xu hướng sụp đổ tồi tệ khi bot quá chậm để phản hồi.
Không hiểu biết về Python, tôi đã không sửa nó, nhưng nó phải làm với việc đọc một câu trả lời trống sau khi hết thời gian trên stdin.

Một cải tiến nhỏ sẽ là đưa đầu ra stderr vào một tệp cho mỗi bot. Điều đó sẽ dễ dàng truy tìm để gỡ lỗi sau khi chết.

Ngoại trừ những vấn đề nhỏ này, tôi thấy Thẩm phán rất đơn giản và dễ sử dụng.
Kudos cho một thách thức sáng tạo và vui vẻ.

Mật mã

#define _CRT_SECURE_NO_WARNINGS // prevents Microsoft from croaking about the safety of scanf. Since every rabid Russian hacker and his dog are welcome to try and overflow my buffers, I could not care less.
#include "lodepng.h"
#include <vector>
#include <deque>
#include <iostream>
#include <sstream>
#include <cassert>   // paranoid android
#include <cstdint>   // fixed size types
#include <algorithm> // min max

using namespace std;

// ============================================================================
// The less painful way I found to teach C++ how to handle png images
// ============================================================================
typedef unsigned tRGB;
#define RGB(r,g,b) (((r) << 16) | ((g) << 8) | (b))
class tRawImage {
public:
    unsigned w, h;

    tRawImage(unsigned w=0, unsigned h=0) : w(w), h(h), data(w*h * 4, 0) {}
    void read(const char* filename) { unsigned res = lodepng::decode(data, w, h, filename); assert(!res);  }
    void write(const char * filename)
    {
        std::vector<unsigned char> png;
        unsigned res = lodepng::encode(png, data, w, h, LCT_RGBA); assert(!res);
        lodepng::save_file(png, filename);
    }
    tRGB get_pixel(int x, int y) const
    {
        size_t base = raw_index(x,y);
        return RGB(data[base], data[base + 1], data[base + 2]);
    }
    void set_pixel(int x, int y, tRGB color)
    {
        size_t base = raw_index(x, y);
        data[base+0] = (color >> 16) & 0xFF;
        data[base+1] = (color >>  8) & 0xFF;
        data[base+2] = (color >> 0) & 0xFF;
        data[base+3] = 0xFF; // alpha
    }
private:
    vector<unsigned char> data;
    void bound_check(unsigned x, unsigned y) const { assert(x < w && y < h); }
    size_t raw_index(unsigned x, unsigned y) const { bound_check(x, y); return 4 * (y * w + x); }
};

// ============================================================================
// coordinates
// ============================================================================
typedef int16_t tCoord;

struct tPoint {
    tCoord x, y;
    tPoint operator+  (const tPoint & p) const { return { x + p.x, y + p.y }; }
};

typedef deque<tPoint> tPointList;

// ============================================================================
// command line and input parsing
// (in a nice airtight bag to contain the stench of C++ string handling)
// ============================================================================
enum tCommand {
    c_quit,
    c_update,
    c_play,
};

class tParser {
public:
    tRGB color;
    tPointList points;

    tRGB read_color(const char * s)
    {
        int r, g, b;
        sscanf(s, "(%d,%d,%d)", &r, &g, &b);
        return RGB(r, g, b);
    }

    tCommand command(void)
    {
        string line;
        getline(cin, line);

        string cmd = get_token(line);
        points.clear();

        if (cmd == "exit") return c_quit;
        if (cmd == "pick") return c_play;

        // even more convoluted and ugly than the LEFT$s and RIGHT$s of Apple ][ basic...
        if (cmd != "colour")
        {
            cerr << "unknown command '" << cmd << "'\n";
            exit(0);
        }
        assert(cmd == "colour");
        color = read_color(get_token(line).c_str());
        get_token(line); // skip "chose"
        while (line != "")
        {
            string coords = get_token(line);
            int x = atoi(get_token(coords, ',').c_str());
            int y = atoi(coords.c_str());
            points.push_back({ x, y });
        }
        return c_update;
    }

private:
    // even more verbose and inefficient than setting up an ADA rendezvous...
    string get_token(string& s, char delimiter = ' ')
    {
        size_t pos = 0;
        string token;
        if ((pos = s.find(delimiter)) != string::npos)
        {
            token = s.substr(0, pos);
            s.erase(0, pos + 1);
            return token;
        }
        token = s; s.clear(); return token;
    }
};

// ============================================================================
// pathing
// ============================================================================
class tPather {

public:
    tPather(tRawImage image, tRGB own_color)
        : arena(image)
        , w(image.w)
        , h(image.h)
        , own_color(own_color)
        , enemy_threat(false)
    {
        // extract colored pixels and own color areas
        tPointList own_pixels;
        color_plane[neutral].resize(w*h, false);
        color_plane[enemies].resize(w*h, false);
        for (size_t x = 0; x != w; x++)
        for (size_t y = 0; y != h; y++)
        {
            tRGB color = image.get_pixel(x, y);
            if (color == col_white) continue;
            plane_set(neutral, x, y);
            if (color == own_color) own_pixels.push_back({ x, y }); // fill the frontier with all points of our color
        }

        // compute initial frontier
        for (tPoint pixel : own_pixels)
        for (tPoint n : neighbour)
        {
            tPoint pos = pixel + n;
            if (!in_picture(pos)) continue;
            if (image.get_pixel(pos.x, pos.y) == col_white)
            {
                frontier.push_back(pixel);
                break;
            }
        }
    }

    tPointList search(size_t pixels_required)
    {
        // flood fill the arena, starting from our current frontier
        tPointList result;
        tPlane closed;
        static tCandidate pool[max_size*max_size]; // fastest possible garbage collection
        size_t alloc;
        static tCandidate* border[max_size*max_size]; // a FIFO that beats a deque anytime
        size_t head, tail;
        static vector<tDistance>distance(w*h); // distance map to be flooded
        size_t filling_pixels = 0; // end of game  optimization

    get_more_results:

        // ready the distance map for filling
        distance.assign(w*h, distance_max);

        // seed our flood fill with the frontier
        alloc = head = tail = 0;
        for (tPoint pos : frontier)
        {
            border[tail++] = new (&pool[alloc++]) tCandidate (pos);
        }

        // set already explored points
        closed = color_plane[neutral]; // that's one huge copy

        // add current result
        for (tPoint pos : result)
        {
            border[tail++] = new (&pool[alloc++]) tCandidate(pos);
            closed[raw_index(pos)] = true;
        }

        // let's floooooood!!!!
        while (tail > head && pixels_required > filling_pixels)
        {
            tCandidate& candidate = *border[head++];
            tDistance  dist = candidate.distance;
            distance[raw_index(candidate.pos)] = dist++;
            for (tPoint n : neighbour)
            {
                tPoint pos = candidate.pos + n;
                if (!in_picture (pos)) continue;
                size_t index = raw_index(pos);
                if (closed[index]) continue;
                if (color_plane[enemies][index])
                {
                    if (dist == (distance_initial + 1)) continue; // already near an enemy pixel

                    // reached the nearest enemy pixel
                    static tPoint trail[max_size * max_size / 2]; // dimensioned as a 1 pixel wide spiral across the whole map
                    size_t trail_size = 0;

                    // walk back toward the frontier
                    tPoint walker = candidate.pos;
                    tDistance cur_d = dist;
                    while (cur_d > distance_initial)
                    {
                        trail[trail_size++] = walker;
                        tPoint next_n;
                        for (tPoint n : neighbour)
                        {
                            tPoint next = walker + n;
                            if (!in_picture(next)) continue;
                            tDistance prev_d = distance[raw_index(next)];
                            if (prev_d < cur_d)
                            {
                                cur_d = prev_d;
                                next_n = n;
                            }
                        }
                        walker = walker + next_n;
                    }

                    // collect our precious new pixels
                    if (trail_size > 0)
                    {
                        while (trail_size > 0)
                        {
                            if (pixels_required-- == 0) return result;       // ;!; <-- BRUTAL EXIT
                            tPoint pos = trail[--trail_size];
                            result.push_back (pos);
                        }
                        goto get_more_results; // I could have done a loop, but I did not bother to. Booooh!!!
                    }
                    continue;
                }

                // on to the next neighbour
                closed[index] = true;
                border[tail++] = new (&pool[alloc++]) tCandidate(pos, dist);
                if (!enemy_threat) filling_pixels++;
            }
        }

        // if all enemies have been surrounded, top up result with the first points of our flood fill
        if (enemy_threat) enemy_threat = pixels_required == 0;
        tPathIndex i = frontier.size() + result.size();
        while (pixels_required--) result.push_back(pool[i++].pos);
        return result;
    }

    // tidy up our map and frontier while other bots are thinking
    void validate(tPointList moves)
    {
        // report new points
        for (tPoint pos : moves)
        {
            frontier.push_back(pos);
            color_plane[neutral][raw_index(pos)] = true;
        }

        // remove surrounded points from frontier
        for (auto it = frontier.begin(); it != frontier.end();) 
        {
            bool in_frontier = false;
            for (tPoint n : neighbour)
            {
                tPoint pos = *it + n;
                if (!in_picture(pos)) continue;
                if (!(color_plane[neutral][raw_index(pos)] || color_plane[enemies][raw_index(pos)]))
                {
                    in_frontier = true;
                    break;
                }
            }
            if (!in_frontier) it = frontier.erase(it); else ++it; // the magic way of deleting an element without wrecking your iterator
        }       
    }

    // handle enemy move notifications
    void update(tRGB color, tPointList points)
    {
        assert(color != own_color);

        // plot enemy moves
        enemy_threat = true;
        for (tPoint p : points) plane_set(enemies, p);

        // important optimization here :
        /*
         * Stop 1 pixel away from the enemy to avoid wasting moves in dogfights.
         * Better let the enemy gain a few more pixels inside the surrounded region
         * and use our precious moves to get closer to the next threat.
         */
        for (tPoint p : points) for (tPoint n : neighbour) plane_set(enemies, p+n);

        // if a new enemy is detected, gather its initial pixels
        for (tRGB enemy : known_enemies) if (color == enemy) return;
        known_enemies.push_back(color);
        tPointList start_areas = scan_color(color);
        for (tPoint p : start_areas) plane_set(enemies, p);
    }

private:
    typedef uint16_t tPathIndex;

    typedef uint16_t tDistance;
    static const tDistance distance_max     = 0xFFFF;
    static const tDistance distance_initial = 0;

    struct tCandidate {
        tPoint pos;
        tDistance distance;
        tCandidate(){} // must avoid doing anything in this constructor, or pathing will slow to a crawl
        tCandidate(tPoint pos, tDistance distance = distance_initial) : pos(pos), distance(distance) {}
    };

    // neighbourhood of a pixel
    static const tPoint neighbour[4];

    // dimensions
    tCoord w, h; 
    static const size_t max_size = 1000;

    // colors lookup
    const tRGB col_white = RGB(0xFF, 0xFF, 0xFF);
    const tRGB col_black = RGB(0x00, 0x00, 0x00);
    tRGB own_color;
    const tRawImage arena;
    tPointList scan_color(tRGB color)
    {
        tPointList res;
        for (size_t x = 0; x != w; x++)
        for (size_t y = 0; y != h; y++)
        {
            if (arena.get_pixel(x, y) == color) res.push_back({ x, y });
        }
        return res;
    }

    // color planes
    typedef vector<bool> tPlane;
    tPlane color_plane[2];
    const size_t neutral = 0;
    const size_t enemies = 1;
    bool plane_get(size_t player, tPoint p) { return plane_get(player, p.x, p.y); }
    bool plane_get(size_t player, size_t x, size_t y) { return in_picture(x, y) ? color_plane[player][raw_index(x, y)] : false; }
    void plane_set(size_t player, tPoint p) { plane_set(player, p.x, p.y); }
    void plane_set(size_t player, size_t x, size_t y) { if (in_picture(x, y)) color_plane[player][raw_index(x, y)] = true; }
    bool in_picture(tPoint p) { return in_picture(p.x, p.y); }
    bool in_picture(int x, int y) { return x >= 0 && x < w && y >= 0 && y < h; }
    size_t raw_index(tPoint p) { return raw_index(p.x, p.y); }
    size_t raw_index(size_t x, size_t y) { return y*w + x; }

    // frontier
    tPointList frontier;

    // register enemies when they show up
    vector<tRGB>known_enemies;

    // end of game optimization
    bool enemy_threat;
};

// small neighbourhood
const tPoint tPather::neighbour[4] = { { -1, 0 }, { 1, 0 }, { 0, -1 }, { 0, 1 } };

// ============================================================================
// main class
// ============================================================================
class tGame {
public:
    tGame(tRawImage image, tRGB color, size_t num_pixels)
        : own_color(color)
        , response_len(num_pixels)
        , pather(image, color)
    {}

    void main_loop(void)
    {
        // grab an initial answer in case we're playing first
        tPointList moves = pather.search(response_len);
        for (;;)
        {
            ostringstream answer;
            size_t        num_points;
            tPointList    played;

            switch (parser.command())
            {
            case c_quit: 
                return;

            case c_play:
                // play as many pixels as possible
                if (moves.size() < response_len) moves = pather.search(response_len);
                num_points = min(moves.size(), response_len);
                for (size_t i = 0; i != num_points; i++)
                {
                    answer << moves[0].x << ',' << moves[0].y;
                    if (i != num_points - 1) answer << ' '; // STL had more important things to do these last 30 years than implement an implode/explode feature, but you can write your own custom version with exception safety and in-place construction. It's a bit of work, but thanks to C++ inherent genericity you will be able to extend it to giraffes and hippos with a very manageable amount of code refactoring. It's not anyone's language, your C++, eh. Just try to implode hippos in Python. Hah!
                    played.push_back(moves[0]);
                    moves.pop_front();
                }
                cout << answer.str() << '\n';

                // now that we managed to print a list of points to stdout, we just need to cleanup the mess
                pather.validate(played);
                break;

            case c_update:
                if (parser.color == own_color) continue; // hopefully we kept track of these already
                pather.update(parser.color, parser.points);
                moves = pather.search(response_len); // get cracking
                break;
            }
        }
    }

private:
    tParser parser;
    tRGB    own_color;
    size_t  response_len;
    tPather pather;
};

void main(int argc, char * argv[])
{
    // process command line
    tRawImage raw_image; raw_image.read (argv[1]);
    tRGB my_color = tParser().read_color(argv[2]);
    int num_pixels               = atoi (argv[3]);

    // init and run
    tGame game (raw_image, my_color, num_pixels);
    game.main_loop();
}

Xây dựng thực thi

Tôi đã sử dụng LODEpng.cppLODEpng.h để đọc hình ảnh png.
Về cách dễ nhất tôi tìm thấy để dạy ngôn ngữ C ++ chậm phát triển này cách đọc một bức tranh mà không phải xây dựng nửa tá thư viện.
Chỉ cần biên dịch và liên kết LODEpng.cpp cùng với chính và Bob là chú của bạn.

Tôi đã biên dịch với MSVC2013, nhưng vì tôi chỉ sử dụng một vài thùng chứa cơ bản STL (deque và vectơ), nên nó có thể hoạt động với gcc (nếu bạn may mắn).
Nếu không, tôi có thể thử bản dựng MinGW, nhưng thật lòng tôi đang mệt mỏi với các vấn đề về tính di động của C ++.

Tôi đã thực hiện khá nhiều C / C ++ di động trong những ngày của mình (trên các trình biên dịch kỳ lạ cho các bộ xử lý 8 đến 32 bit khác nhau cũng như SunOS, Windows từ 3.11 cho đến Vista và Linux từ khi còn nhỏ cho đến ngựa vằn Ubuntu hay bất cứ điều gì, vì vậy tôi nghĩ Tôi có một ý tưởng khá hay về ý nghĩa của tính di động), nhưng tại thời điểm đó, nó không yêu cầu ghi nhớ (hoặc khám phá) vô số sự khác biệt giữa cách diễn giải của GNU và Microsoft về thông số kỹ thuật khó hiểu và khó hiểu của quái vật STL.

Kết quả chống lại Swallower

đấu trường1 đấu trường2 đấu trường3 đấu trường4

Làm thế nào nó hoạt động

Tại cốt lõi, đây là một con đường tràn ngập vũ phu đơn giản.

Đường biên của màu của người chơi (tức là các pixel có ít nhất một hàng xóm trắng) được sử dụng làm hạt giống để thực hiện thuật toán ngập khoảng cách cổ điển.

Khi một điểm đạt đến độ sáng của màu kẻ thù, một đường lùi được tính toán để tạo ra một chuỗi pixel di chuyển về phía điểm địch gần nhất.

Quá trình được lặp lại cho đến khi đủ điểm được thu thập để đáp ứng độ dài mong muốn.

Sự lặp lại này rất tốn kém, đặc biệt là khi chiến đấu gần kẻ thù.
Mỗi khi tìm thấy một chuỗi pixel dẫn từ biên giới đến pixel đối phương (và chúng tôi cần nhiều điểm hơn để hoàn thành câu trả lời), lấp đầy lũ được làm lại từ đầu, với đường dẫn mới được thêm vào biên giới. Điều đó có nghĩa là bạn có thể phải thực hiện 5 lần lấp đầy lũ trở lên để có câu trả lời 10 pixel.

Nếu không thể tiếp cận được nhiều pixel đối phương hơn, hàng xóm tùy ý của các pixel biên giới sẽ được chọn.
Thuật toán phá hủy một vùng lũ khá kém hiệu quả, nhưng điều này chỉ xảy ra sau khi kết quả của trò chơi đã được quyết định (tức là không có lãnh thổ trung lập nào để đấu tranh).
Tôi đã tối ưu hóa nó để Thẩm phán không mất nhiều thời gian để lấp đầy bản đồ một khi cuộc thi đã được giải quyết. Ở trạng thái hiện tại, thời gian thực hiện là không đáng kể so với chính Thẩm phán.

Vì màu sắc của kẻ thù không được biết đến khi bắt đầu, hình ảnh đấu trường ban đầu được lưu giữ để sao chép khu vực bắt đầu của kẻ thù khi nó di chuyển lần đầu tiên.
Nếu mã phát đầu tiên, nó sẽ chỉ lấp đầy một vài pixel tùy ý.

Điều này làm cho thuật toán có khả năng chiến đấu với số lượng đối thủ tùy ý và thậm chí có thể có những đối thủ mới đến một thời điểm ngẫu nhiên hoặc màu sắc xuất hiện mà không có khu vực bắt đầu (mặc dù điều này hoàn toàn không có sử dụng thực tế).

Xử lý kẻ thù trên cơ sở màu mỗi màu cũng sẽ cho phép có hai trường hợp bot hợp tác (sử dụng tọa độ pixel để vượt qua dấu hiệu nhận dạng bí mật).
Nghe có vẻ vui, có lẽ tôi sẽ thử nó :).

Đường dẫn nặng tính toán được thực hiện ngay khi có dữ liệu mới (sau thông báo di chuyển) và một số tối ưu hóa (cập nhật biên giới) được thực hiện ngay sau khi phản hồi được đưa ra (để thực hiện càng nhiều tính toán càng tốt trong các bot khác ).

Ở đây một lần nữa, có thể có nhiều cách để làm những điều tinh tế hơn nếu có nhiều hơn 1 đối thủ (như hủy bỏ tính toán nếu có dữ liệu mới), nhưng với bất kỳ giá nào tôi cũng không thấy cần đa nhiệm ở đâu, miễn là thuật toán được có thể làm việc trên tải đầy đủ.

Vấn đề hiệu năng

Tất cả điều này không thể hoạt động mà không truy cập dữ liệu nhanh (và sức mạnh tính toán nhiều hơn toàn bộ chương trình Appolo, tức là PC trung bình của bạn khi được sử dụng để làm nhiều hơn là đăng một vài tweet).

Tốc độ phụ thuộc rất nhiều vào trình biên dịch. Thông thường GNU đánh bại Microsoft với tỷ lệ 30% (đó là con số kỳ diệu mà tôi nhận thấy trên 3 thách thức mã liên quan đến đường dẫn khác), nhưng tất nhiên số dặm này có thể khác nhau.

Mã ở trạng thái hiện tại của nó hầu như không bị đổ mồ hôi trên đấu trường 4. Máy đo tốc độ Windows báo cáo mức sử dụng CPU khoảng 4 đến 7%, do đó, nó có thể đối phó với bản đồ 1000x1000 trong giới hạn thời gian phản hồi 100ms.

Trọng tâm của mọi thuật toán đường dẫn là một FIFO (có thể được chia theo tỷ lệ, mặc dù không phải trong trường hợp đó), do đó đòi hỏi phân bổ phần tử nhanh.

Vì OP bắt buộc đặt giới hạn cho kích thước đấu trường, tôi đã thực hiện một số phép toán và thấy rằng các cấu trúc dữ liệu cố định có kích thước tối đa (tức là 1.000.000 pixel) sẽ không tiêu thụ nhiều hơn vài chục megabyte, mà PC trung bình của bạn ăn vào bữa sáng.
Thật vậy, dưới Win7 và được biên dịch với MSVC 2013, mã tiêu thụ khoảng 14Mb trên đấu trường 4, trong khi hai luồng của Swallower đang sử dụng hơn 20Mb.

Tôi đã bắt đầu với các bộ chứa STL để tạo mẫu dễ dàng hơn, nhưng STL làm cho mã thậm chí ít đọc hơn, vì tôi không muốn tạo một lớp để gói từng dữ liệu để che giấu sự che giấu đi (cho dù đó là do tôi không có khả năng đối phó với STL được để lại cho sự đánh giá cao của người đọc).
Bất kể, kết quả rất chậm chạp đến mức ban đầu tôi nghĩ rằng tôi đang xây dựng một phiên bản gỡ lỗi do nhầm lẫn.

Tôi cho rằng điều này một phần là do việc triển khai STL của Microsoft cực kỳ kém (ví dụ, vectơ và bitcoin thực hiện kiểm tra ràng buộc hoặc các hoạt động crypic khác trên toán tử [], vi phạm trực tiếp thông số kỹ thuật) và một phần do thiết kế STL chinh no.

Tôi có thể đối phó với các vấn đề về cú pháp và tính di động (ví dụ Microsoft vs GNU) nếu các màn trình diễn ở đó, nhưng điều này chắc chắn không phải là trường hợp.

Ví dụ, dequevốn đã chậm, vì nó xáo trộn rất nhiều dữ liệu sổ sách xung quanh chờ đợi dịp để thực hiện thay đổi kích thước siêu thông minh của nó, về điều mà tôi không thể quan tâm.
Chắc chắn tôi có thể đã triển khai một cấp phát tùy chỉnh và phân bổ các bit mẫu tùy chỉnh khác, nhưng một mình phân bổ tùy chỉnh tốn vài trăm dòng mã và phần tốt hơn trong một ngày để kiểm tra, với hàng tá giao diện mà nó phải thực hiện, trong khi một cấu trúc tương đương thủ công là khoảng 0 dòng mã (mặc dù nguy hiểm hơn, nhưng thuật toán sẽ không hoạt động nếu tôi không biết - hoặc nghĩ rằng tôi biết - dù sao tôi cũng đang làm gì).

Vì vậy, cuối cùng tôi đã giữ các thùng chứa STL trong các phần không quan trọng của mã, và xây dựng bộ cấp phát tàn bạo của riêng tôi và FIFO với hai mảng khoảng năm 1970 và ba quần short không dấu.

Nuốt nuốt

Như tác giả của nó đã xác nhận, các mẫu thất thường của Swallower là do độ trễ giữa các thông báo di chuyển của kẻ thù và các bản cập nhật từ chuỗi đường dẫn.
Máy đo lưu lượng hệ thống cho thấy rõ ràng đường dẫn tiêu thụ CPU 100% mọi lúc và các mẫu hình răng cưa có xu hướng xuất hiện khi trọng tâm của cuộc chiến chuyển sang một khu vực mới. Điều này cũng khá rõ ràng với các hình ảnh động.

Tối ưu hóa đơn giản nhưng hiệu quả

Sau khi xem các trận đấu chó hoành tráng giữa Swallower và máy bay chiến đấu của tôi, tôi nhớ một câu nói cũ từ trò chơi cờ vây: phòng thủ cận cảnh, nhưng tấn công từ xa.

Có sự khôn ngoan trong đó. Nếu bạn cố gắng bám lấy đối thủ của mình quá nhiều, bạn sẽ lãng phí những bước đi quý giá khi cố gắng chặn từng con đường có thể. Ngược lại, nếu bạn chỉ cách một pixel, bạn có thể sẽ tránh lấp đầy những khoảng trống nhỏ sẽ thu được rất ít và sử dụng các bước di chuyển của mình để chống lại các mối đe dọa quan trọng hơn.

Để thực hiện ý tưởng này, tôi chỉ cần mở rộng các bước di chuyển của kẻ thù (đánh dấu 4 hàng xóm của mỗi lần di chuyển là pixel của kẻ thù).
Điều này ngăn thuật toán đường dẫn cách xa biên giới của kẻ thù một pixel, cho phép máy bay chiến đấu của tôi hoạt động xung quanh một kẻ thù mà không bị vướng vào quá nhiều trận đấu chó.

Bạn có thể thấy sự cải thiện
(mặc dù tất cả các lần chạy đều không thành công, bạn có thể nhận thấy các phác thảo mượt mà hơn nhiều):

trước sau


1
Ồ Tôi nghĩ không có gì sẽ đánh bại Swallower. Giải pháp tuyệt vời với mô tả tuyệt vời. Tôi nhớ K & R C từ những ngày xưa tốt đẹp, nhưng rồi C lại đi vào mặt tối. Tôi nghĩ bạn sẽ thích Python .
Logic Knight

Đó là một niềm vui thực sự để giải quyết một thách thức vì vậy ... tốt .. thử thách và vui vẻ. Điều này cho phép tôi kiểm tra viên ngọc nhỏ LODEpng đầy đủ này và kết quả rất hứa hẹn rằng tôi có thể xem lại tay đua png, thử nghiệm lại mối quan hệ yêu / ghét của tôi với C. hậu kỳ khét tiếng này

1
Nuốt đôi khi hơi thất thường để giữ trong thời gian giới hạn. Đây là một phần những gì đa luồng dành cho. Làm tốt lắm!! Tôi nghĩ rằng tôi sẽ nhân đôi số tiền thưởng của mình ...
TheNumberOne

1
Gối có tải xuống cho 64 bit. Nó có thể được sử dụng giống như PIL.
TheNumberOne

@TheBestOne tôi cũng nghĩ vậy. Họa sĩ tàn bạo của tôi tận dụng những khoảnh khắc này nơi người nuốt của bạn nhai dữ liệu lỗi thời :). Đối với PIL, tôi đã tải xuống về tất cả các phiên bản Amd64 PIL và Gối có sẵn trên World Wide Web, nhưng chúng không hoạt động với Python 63,5 bit cốt lõi của tôi, có lẽ là phiên bản lỗi và / hoặc lỗi thời. Dù sao, cổng Win32 cũng hoạt động tốt và nếu một ngày nào đó tôi cần thứ gì đó nhanh hơn tôi sẽ phải chuyển sang PyPy.

21

Blob sâu đầu tiên so với Blob

Ngôn ngữ = Python (3.2)

Điểm = 111.475388276 153.34210035

Cập nhật: Bây giờ sử dụng một Setlớp tùy chỉnh để có được pop()phương thức tạo ra một kiểu mẫu lưới giúp cải thiện đáng kể diện tích được bao phủ ngay từ đầu, cắt bỏ phần lớn hình ảnh khỏi kẻ thù. Lưu ý: Tôi đang sử dụng lưới 12 x 12 cho mẫu lưới kích thước lưới ngẫu nhiên này có vẻ mang lại kết quả tốt nhất cho Arena3 (mạng có điểm số kém nhất trước khi cập nhật), tuy nhiên rất có thể là tối ưu hơn kích thước lưới tồn tại cho sự lựa chọn của đấu trường.

Tôi đã tiến hành một sửa đổi đơn giản cho bot tham chiếu để làm cho nó ưu tiên chọn các điểm khả thi được bao quanh bởi càng ít điểm màu riêng càng tốt. Một cải tiến có thể là làm cho nó cũng ưu tiên chọn các điểm khả thi được bao quanh bởi càng nhiều điểm màu kẻ thù càng tốt.

dfblob.py:

import sys, os
from PIL import Image

class RoomyIntPairHashSet:
    def __init__(self, firstMax, secondMax):
        self.m1 = firstMax
        self.m2 = secondMax
        self.set = [set() for i in range((firstMax - 1) * (secondMax - 1) + 1)]
        self.len = 0

    def add(self, tup):
        subset = self.set[self.gettuphash(tup)]
        self.len -= len(subset)
        subset.add(tup)
        self.len += len(subset)

    def discard(self, tup):
        subset = self.set[self.gettuphash(tup)]
        self.len -= len(subset)
        subset.discard(tup)
        self.len += len(subset)

    def pop(self):
        for s in self.set:
            if len(s) > 0:
                self.len -= 1
                return s.pop()
        return self.set[0].pop()

    def gettuphash(self, tup):
        return (tup[0] % self.m1) * (tup[1] % self.m2)

    def __len__(self):
        return self.len

gridhashwidth = 12
gridhashheight = 12
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, virtualneighbors, colour, num_neighbors):
    if pix[loc] == (255,255,255):
        plist = [(loc[0]+dx, loc[1]+dy) for dx,dy in ORTH]
        actual_num_neighbors = 0
        for p in plist:
            if 0<=p[0]<W and 0<=p[1]<H and pix[p]==colour or p in virtualneighbors:
                actual_num_neighbors += 1
        return num_neighbors == actual_num_neighbors
    return False

def near(loc, exclude):
    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) and p not in exclude]

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
        for i in range(len(skins)):
            skins[i].discard(p)
        if colour == mycolour:
            for np in near(p, []):
                for j in range(len(skins)):
                    skins[j].discard(np)
                    if canchoose(np, [], mycolour, j + 1):
                        skins[j].add(np)


board = [(x,y) for x in range(W) for y in range(H)]
skins = []
for i in range(1, 1 + len(ORTH)):
    skin = RoomyIntPairHashSet(gridhashwidth, gridhashheight)
    skins.append(skin)
    for p in board:
        if canchoose(p, [], mycolour, i):
            skin.add(p)

while 1:
    msg = sys.stdin.readline()
    print("got message "+ msg, file=sys.stderr)
    if msg.startswith('colour'):
        print("updating image", file=sys.stderr)
        updateimage(image, msg.strip())
        print("updated image", file=sys.stderr)
    if msg.startswith('pick'):
        moves = []
        print("picking moves", file=sys.stderr)
        virtualskins = [RoomyIntPairHashSet(gridhashwidth, gridhashheight) for i in range(len(skins))]
        for i in range(pixbatch):
            for j in range(len(skins)):
                if len(virtualskins[j]) > 0 or len(skins[j]) > 0:
                    move = None
                    if len(virtualskins[j]) > 0:
                        move = virtualskins[j].pop()
                    else:
                        move = skins[j].pop()
                    moves.append(move)
                    print("picking move (%u,%u) " % move, file=sys.stderr)
                    for p in near(move, moves):
                        for k in range(len(skins)):
                            virtualskins[k].discard(p)
                            if canchoose(p, moves, mycolour, k + 1):
                                virtualskins[k].add(p)
                    break
        movetext = ' '.join('%u,%u'%p for p in moves)
        print("picked %u moves" % (len(moves)), file=sys.stderr)
        sys.stdout.write(movetext + '\n')
        sys.stdout.flush()
    if msg.startswith('exit') or len(msg) < 1:
        break

image.save('dfblob.png')

Thẩm phán ban đầu đã được sửa đổi một chút để hoạt động với Python 3.2 (và để thêm chức năng ghi nhật ký thô vào bot + lưu hình ảnh của đấu trường theo định kỳ để tạo hoạt hình):

import sys, re, random, os, shutil, subprocess, datetime, time, signal, io
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)
        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
        shutil.copy(self.prog, self.env)
        shutil.copy(imagename, self.env)
        os.chdir(self.env)
        args = self.call + [imagename, self.colstr, str(PIXELBATCH)]
        errorfile = 'err.log'
        with io.open(errorfile, 'wb') as errorlog:
            self.proc = subprocess.Popen(args, stdin=subprocess.PIPE, 
                stdout=subprocess.PIPE, stderr=errorlog)
        os.chdir('..')
    def send(self, msg):
        if self.faults < FAULTLIMIT:
            self.proc.stdin.write((msg+'\n').encode('utf-8'))
            self.proc.stdin.flush()
    def read(self, timelimit):
        if self.faults < FAULTLIMIT:
            start = time.time()
            inline = self.proc.stdout.readline().decode('utf-8')
            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
os.mkdir('results')

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))
        image.save("results/"+BATTLE+str(turn//100).zfill(3)+'.png')
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')

Các kết quả đấu trường theo sau. Bot dfblob đã được đưa ra màu đỏ cho tất cả các đấu trường.

Đấu trường 1:

Bot dfblob.py with colour (255, 0, 0) scored 163.75666666666666
Bot blob.py with colour (0, 255, 0) scored 14.896666666666667

1

Đấu trường 2:

Bot blob.py with colour (0, 255, 0) scored 17.65563547726219
Bot dfblob.py with colour (255, 0, 0) scored 149.57006774236964

2

Đấu trường 3:

Bot blob.py with colour (0, 255, 0) scored 21.09758208782965
Bot dfblob.py with colour (255, 0, 0) scored 142.9732433108277

3

Đấu trường 4:

Bot blob.py with colour (0, 255, 0) scored 34.443810082244205
Bot dfblob.py with colour (255, 0, 0) scored 157.0684236785121

4


Thuật toán của bạn giống như thuật toán tôi đã triển khai trong Boxer anh em mạnh hơn của Blob. Tôi sẽ sử dụng Boxer nếu Blob không đủ thử thách. Hoạt hình rất đẹp quá.
Logic Knight

Để sử dụng PIL trong python 3, bạn có đang sử dụng gối không?
trichoplax

@githubphagocyte Có
SamYonnou

Bạn đã sử dụng phần mềm nào để tạo những GIF đó?
TheNumberOne

1
@TheBestOne Tôi đã sử dụng ImageMagick một cách cụ thể lệnh convert -delay 5 -loop 0 result*.png animated.gifmặc dù một số gifs sau đó phải được cắt thủ công để được tải lên ở đây
SamYonnou

18

Nuốt

Ngôn ngữ = Java

Điểm = 162.3289512601408075 169.4020975612382575

Tìm kiếm kẻ thù ra ngoài và bao quanh. Bạn có thể phải cho nó một giới hạn thời gian dài hơn. Có thể được cải thiện một chút. Đôi khi in các pixel không hợp lệ.

Cập nhật: Xung quanh nhanh hơn nhiều. Sử dụng một chủ đề khác để cập nhật các ưu tiên. Luôn trả về trong vòng 1 giây. Điểm số không thể đánh bại mà không tăng MAX_TURNS.

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;
import java.util.List;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;

public class Swallower {

    static final byte MY_TYPE = 1;
    static final byte BLANK_TYPE = 0;
    static final byte NEUTRAL_TYPE = 2;
    static final byte ENEMY_TYPE = 3;
    private static final int WHITE = Color.WHITE.getRGB();
    private static final int MAX_TIME = 50;
    private final int color;
    private final int N;
    private final int width;
    private final int height;
    private final BufferedReader in;
    Lock borderLock;
    private final PriorityBlockingQueue<Pixel> border;
    private final Set<Pixel> borderSet;
    private final Thread updater;

    Lock imageLock;
    volatile byte[][] image;
    Lock priorityLock;
    volatile int[][] priority;
    volatile boolean updating;
    volatile private boolean exit;

    class Pixel implements Comparable<Pixel> {

        int x;
        int y;

        public Pixel(int x, int y) {
            this.x = x;
            this.y = y;
        }

        @Override
        public int compareTo(Pixel o) {
            return priority() - o.priority();
        }

        private int priority() {
            priorityLock.lock();
            int p = priority[x][y];
            priorityLock.unlock();
            return p;
        }

        public byte type() {
            imageLock.lock();
            byte i = image[x][y];
            imageLock.unlock();
            return i;
        }

        public boolean isBorder() {
            if (type() != BLANK_TYPE){
                return false;
            }
            for (Pixel p : pixelsAround()){
                if (p.type() == MY_TYPE){
                    return true;
                }
            }
            return false;
        }

        public void setType(byte newType) {
            imageLock.lock();
            image[x][y] = newType;
            imageLock.unlock();
        }

        public void setPriority(int newPriority) {
            borderLock.lock();
            boolean contains = borderSet.remove(this);
            if (contains){
                border.remove(this);
            }
            priorityLock.lock();
            priority[x][y] = newPriority;
            priorityLock.unlock();
            if (contains){
                border.add(this);
                borderSet.add(this);
            }
            borderLock.unlock();
        }

        public List<Pixel> pixelsAround() {
            List<Pixel> pixels = new ArrayList<>(4);
            if (x > 0){
                pixels.add(new Pixel(x - 1, y));
            }
            if (x < width - 1){
                pixels.add(new Pixel(x + 1, y));
            }
            if (y > 0){
                pixels.add(new Pixel(x, y - 1));
            }
            if (y < height - 1){
                pixels.add(new Pixel(x, y + 1));
            }
            return pixels;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;

            Pixel pixel = (Pixel) o;

            return x == pixel.x && y == pixel.y;

        }

        @Override
        public int hashCode() {
            int result = x;
            result = 31 * result + y;
            return result;
        }
    }

    public static void main(String[] args) throws IOException {
        BufferedImage image = ImageIO.read(new File(args[0]));
        int color = parseColorString(args[1]);
        int N = Integer.parseInt(args[2]);
        new Swallower(image, color, N).start();
    }

    private void start() throws IOException {
        updater.start();
        try {
            while (true) {
                String input = in.readLine();
                if (input.equals("exit")) {
                    exit = true;
                    if (!updating) {
                        updater.interrupt();
                    }
                    return;
                } else if (input.startsWith("colour")) {
                    updateImage(input);
                } else if (input.equals("pick pixels")) {
                    if (updating) {
                        try {
                            synchronized (Thread.currentThread()){
                                Thread.currentThread().wait(MAX_TIME);
                            }
                        } catch (InterruptedException ignored) {
                        }
                    }
                    for (int i = 0; i < N && !border.isEmpty(); i++) {
                        borderLock.lock();
                        Pixel p = border.poll();
                        borderSet.remove(p);
                        borderLock.unlock();
                        if (!p.isBorder()){
                            i--;
                            continue;
                        }
                        updateImage(MY_TYPE, p);
                        System.out.print(p.x + "," + p.y + " ");
                    }
                    System.out.println();
                }
            }
        } catch (Throwable e){
            exit = true;
            if (!updating){
                updater.interrupt();
            }
            throw e;
        }
    }

    private void updateImage(byte type, Pixel... pixels) {
        for (Pixel pixel : pixels){
            pixel.setType(type);
            if (type == MY_TYPE){
                pixel.setPriority(Integer.MAX_VALUE);
            } else {
                pixel.setPriority(0);
            }
        }
        for (Pixel pixel : pixels){
            for (Pixel p : pixel.pixelsAround()){
                if (p.type() == BLANK_TYPE){
                    addPixelToUpdate(p);
                }
                if (type == MY_TYPE && p.isBorder()){
                    borderLock.lock();
                    if (borderSet.add(p)){
                        border.add(p);
                    }
                    borderLock.unlock();
                }
            }
        }
    }

    private synchronized void addPixelToUpdate(Pixel p) {
        if (pixelsToUpdateSet.add(p)) {
            pixelsToUpdate.add(p);
            if (!updating){
                updater.interrupt();
            }
        }
    }

    Queue<Pixel> pixelsToUpdate;
    Set<Pixel> pixelsToUpdateSet;

    private void update(){
        while (true){
            if (exit){
                return;
            }
            if (pixelsToUpdate.isEmpty()){
                try {
                    updating = false;
                    while (!exit) {
                        synchronized (Thread.currentThread()) {
                            Thread.currentThread().wait();
                        }
                    }
                } catch (InterruptedException ignored){}
                continue;
            }
            updating = true;
            Pixel pixel = pixelsToUpdate.poll();
            if (pixel.type() != BLANK_TYPE){
                continue;
            }
            pixelsToUpdateSet.remove(pixel);
            updatePixel(pixel);
        }
    }

    private void updatePixel(Pixel pixel) {
        int originalPriority = pixel.priority();
        int minPriority = Integer.MAX_VALUE;
        List<Pixel> pixelsAround = pixel.pixelsAround();
        for (Pixel p : pixelsAround){
            int priority = p.priority();
            if (priority < minPriority){
                minPriority = priority;
            }
        }
        if (minPriority >= originalPriority){
            pixel.setPriority(Integer.MAX_VALUE);
            pixelsToUpdate.addAll(pixelsAround.stream().filter(p -> p.type() == 0 && p.priority() != Integer.MAX_VALUE).filter(pixelsToUpdateSet::add).collect(Collectors.toList()));
        } else {
            pixel.setPriority(minPriority + 1);
            for (Pixel p : pixelsAround){
                if (p.type() == 0 && p.priority() > minPriority + 2){
                    if (pixelsToUpdateSet.add(p)){
                        pixelsToUpdate.add(p);
                    }
                }
            }
        }

    }

    private void updateImage(String input) {
        String[] inputs = input.split("\\s");
        int color = parseColorString(inputs[1]);
        byte type;
        if (color == this.color){
            return;
        } else {
            type = ENEMY_TYPE;
        }
        Pixel[] pixels = new Pixel[inputs.length - 3];
        for (int i = 0; i < inputs.length - 3; i++){
            String[] coords = inputs[i + 3].split(",");
            pixels[i] = new Pixel(Integer.parseInt(coords[0]), Integer.parseInt(coords[1]));
        }
        updateImage(type, pixels);
    }

    private static int parseColorString(String input) {
        String[] colorString = input.split("[\\(\\),]");
        return new Color(Integer.parseInt(colorString[1]), Integer.parseInt(colorString[2]), Integer.parseInt(colorString[3])).getRGB();
    }

    private Swallower(BufferedImage image, int color, int N){
        this.color = color;
        this.N = N;
        this.width = image.getWidth();
        this.height = image.getHeight();
        this.image = new byte[width][height];
        this.priority = new int[width][height];
        for (int x = 0; x < width; x++){
            for (int y = 0; y < height; y++){
                int pixelColor = image.getRGB(x,y);
                priority[x][y] = Integer.MAX_VALUE;
                if (pixelColor == WHITE){
                    this.image[x][y] = BLANK_TYPE;
                } else if (pixelColor == this.color){
                    this.image[x][y] = MY_TYPE;
                } else {
                    this.image[x][y] = NEUTRAL_TYPE;
                }
            }
        }
        border = new PriorityBlockingQueue<>();
        borderSet = Collections.synchronizedSet(new HashSet<>());
        borderLock = new ReentrantLock();
        priorityLock = new ReentrantLock();
        imageLock = new ReentrantLock();
        for (int x = 0; x < width; x++){
            for (int y = 0; y < height; y++){
                Pixel pixel = new Pixel(x,y);
                if (pixel.type() == BLANK_TYPE){
                    if (pixel.isBorder()){
                        if (borderSet.add(pixel)){
                            border.add(pixel);
                        }
                    }
                }
            }
        }
        in = new BufferedReader(new InputStreamReader(System.in));
        updating = false;
        updater = new Thread(this::update);
        pixelsToUpdate = new ConcurrentLinkedQueue<>();
        pixelsToUpdateSet = Collections.synchronizedSet(new HashSet<>());
        exit = false;
    }

}

Làm thế nào nó hoạt động:

Bot này duy trì hàng đợi ưu tiên của pixel mà nó có thể thêm. Mức độ ưu tiên của pixel đối phương là 0. Mức độ ưu tiên của pixel trống lớn hơn 1 so với mức ưu tiên thấp nhất xung quanh nó. Tất cả các pixel khác có mức độ ưu tiên là Integer.MAX_VALUE. Chủ đề cập nhật liên tục cập nhật các ưu tiên của pixel. Mỗi lượt N pixel thấp nhất được bật ra khỏi hàng đợi ưu tiên.

Green Blob vs Red Swallower

Điểm số của Blob = 1.680553372583887225

Điểm của Swallower = 169.4020975612382575

Đấu trường 1:

Bot Blob.py with colour (0, 255, 0) scored 1.2183333333333333
Bot Swallower.class with colour (255, 0, 0) scored 177.435

nhập mô tả hình ảnh ở đây

Đấu trường 2:

Bot Swallower.class with colour (255, 0, 0) scored 149.57829253338517
Bot Blob.py with colour (0, 255, 0) scored 0.5159187091564356

nhập mô tả hình ảnh ở đây

Đấu trường 3:

Bot Blob.py with colour (0, 255, 0) scored 0.727104853136361
Bot Swallower.class with colour (255, 0, 0) scored 163.343720545521

nhập mô tả hình ảnh ở đây

Đấu trường 4:

Bot Swallower.class with colour (255, 0, 0) scored 187.25137716604686
Bot Blob.py with colour (0, 255, 0) scored 4.260856594709419

nhập mô tả hình ảnh ở đây

Green Swallower so với Red Blob

Điểm số của Blob = 1.6852943642218457375

Điểm số của Swallower = 169.3923095387498625

Đấu trường 1:

Bot Blob.py with colour (255, 0, 0) scored 1.3166666666666667
Bot Swallower.class with colour (0, 255, 0) scored 177.33666666666667

nhập mô tả hình ảnh ở đây

Đấu trường 2:

Bot Swallower.class with colour (0, 255, 0) scored 149.57829253338517
Bot Blob.py with colour (255, 0, 0) scored 0.49573058575466195

nhập mô tả hình ảnh ở đây

Đấu trường 3:

Bot Swallower.class with colour (0, 255, 0) scored 163.14367053301788
Bot Blob.py with colour (255, 0, 0) scored 0.9271548656394868

nhập mô tả hình ảnh ở đây

Đấu trường 4:

Bot Swallower.class with colour (0, 255, 0) scored 187.51060842192973
Bot Blob.py with colour (255, 0, 0) scored 4.0016253388265675

nhập mô tả hình ảnh ở đây

Red Swallower vs Green Depth Blob

Điểm của Swallower = 157,0749775233111925

Độ sâu Điểm đầu tiên của Blob = 18.192783547939744

Đấu trường 1:

Bot Swallower.class with colour (255, 0, 0) scored 173.52166666666668
Bot dfblob.py with colour (0, 255, 0) scored 5.131666666666667

nhập mô tả hình ảnh ở đây

Đấu trường 2:

Bot dfblob.py with colour (0, 255, 0) scored 17.25635925887156
Bot Swallower.class with colour (255, 0, 0) scored 149.57829253338517

nhập mô tả hình ảnh ở đây

Đấu trường 3:

Bot Swallower.class with colour (255, 0, 0) scored 153.59801488833747
Bot dfblob.py with colour (0, 255, 0) scored 10.472810510319889

nhập mô tả hình ảnh ở đây

Đấu trường 4:

Bot dfblob.py with colour (0, 255, 0) scored 39.91029775590086
Bot Swallower.class with colour (255, 0, 0) scored 151.60193600485545

nhập mô tả hình ảnh ở đây

Green Swallower vs Red Depth Blob

Điểm của Swallower = 154.3368355651281075

Độ sâu Điểm đầu tiên của Blob = 18.84463249420435425

Đấu trường 1:

Bot Swallower.class with colour (0, 255, 0) scored 165.295
Bot dfblob.py with colour (255, 0, 0) scored 13.358333333333333

nhập mô tả hình ảnh ở đây

Đấu trường 2:

Bot dfblob.py with colour (255, 0, 0) scored 8.91118721119768
Bot Swallower.class with colour (0, 255, 0) scored 149.57829253338517

nhập mô tả hình ảnh ở đây

Đấu trường 3:

Bot Swallower.class with colour (0, 255, 0) scored 157.01136822667206
Bot dfblob.py with colour (255, 0, 0) scored 7.059457171985304

nhập mô tả hình ảnh ở đây

Đấu trường 4:

Bot dfblob.py with colour (255, 0, 0) scored 46.0495522603011
Bot Swallower.class with colour (0, 255, 0) scored 145.4626815004552

nhập mô tả hình ảnh ở đây

Green Blob vs Red Depth Blob đầu tiên vs Blue Swallower:

Điểm số của Blob = 6.347962032393275525

Độ sâu Điểm đầu tiên của Blob = 27.34842554331698275

Điểm của Swallower = 227.720728953415375

Đấu trường 1:

Bot Swallower.class with colour (0, 0, 255) scored 242.54
Bot Blob.py with colour (0, 255, 0) scored 1.21
Bot dfblob.py with colour (255, 0, 0) scored 24.3525

nhập mô tả hình ảnh ở đây

Đấu trường 2:

Bot dfblob.py with colour (255, 0, 0) scored 17.828356088588478
Bot Blob.py with colour (0, 255, 0) scored 0.9252889892479551
Bot Swallower.class with colour (0, 0, 255) scored 224.36743880007776

nhập mô tả hình ảnh ở đây

Đấu trường 3:

Bot dfblob.py with colour (255, 0, 0) scored 7.105141670032893
Bot Swallower.class with colour (0, 0, 255) scored 226.52057245080502
Bot Blob.py with colour (0, 255, 0) scored 12.621905476369092

nhập mô tả hình ảnh ở đây

Đấu trường 4:

Bot dfblob.py with colour (255, 0, 0) scored 60.10770441464656
Bot Blob.py with colour (0, 255, 0) scored 10.634653663956055
Bot Swallower.class with colour (0, 0, 255) scored 217.45490456277872

nhập mô tả hình ảnh ở đây

Dưới đây là thẩm phán của Sam Yonnou với một vài thay đổi để bạn chỉ định các tệp và lệnh riêng biệt:

import sys, re, random, os, shutil, subprocess, datetime, time, signal, io
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, progs, command=None, colour=None):
        self.prog = progs[0]
        self.botlist.append(self)
        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
        for prog in progs:
            shutil.copy(prog, self.env)
        shutil.copy(imagename, self.env)
        os.chdir(self.env)
        args = command + [imagename, self.colstr, str(PIXELBATCH)]
        errorfile = 'err.log'
        with io.open(errorfile, 'wb') as errorlog:
            self.proc = subprocess.Popen(args, stdin=subprocess.PIPE, 
                stdout=subprocess.PIPE, stderr=errorlog)
        os.chdir('..')
    def send(self, msg):
        if self.faults < FAULTLIMIT:
            self.proc.stdin.write((msg+'\n').encode('utf-8'))
            self.proc.stdin.flush()
    def read(self, timelimit):
        if self.faults < FAULTLIMIT:
            start = time.time()
            inline = self.proc.stdout.readline().decode('utf-8')
            if time.time() - start > timelimit:
                self.faults += 1
                inline = ''
            return inline.strip()
    def exit(self):
        self.send('exit')

from cfg import *
for i, (progs, command) in enumerate(botspec):
    Bot(progs, command, colourspec[i])

image = Image.open(imagename)
pix = image.load()
W,H = image.size
resultdirectory = 'results of ' + BATTLE
os.mkdir(resultdirectory)

time.sleep(INITTIME)
total = 0
image.save(resultdirectory+'/'+'result000.png')
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))
        image.save(resultdirectory+'/result'+str(turn//100).zfill(3)+'.png')
image.save(resultdirectory+'/result999.png')
for msgbot in Bot.botlist:
    msgbot.exit()

resultfile = io.open(resultdirectory+'/result.txt','w')
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))
    print('Bot %s with colour %s scored %s' % (bot.prog, bot.colour, score), file=resultfile)
image.save(BATTLE+'.png')

Ví dụ cfg:

BATTLE = 'Green DepthFirstBlob vs Red Swallower @ arena1'
MAXTURNS = 20000
PIXELBATCH = 10
INITTIME = 2.0
TIMELIMIT = .1
FAULTLIMIT = 5

imagename = 'arena1.png'

colourspec = (0,255,0), (255,0,0)

botspec = [
    (['DepthFirstBlob.py'], ['python', 'DepthFirstBlob.py']),
    (['Swallower.class','Swallower$Pixel.class'], ['java', 'Swallower']),
    ]

Lưu ý: Bất cứ ai quản lý để nuốt Swallower đều nhận được 100 tiền danh tiếng. Xin vui lòng gửi trong các ý kiến ​​dưới đây nếu bạn thành công ở đây.


2
@githubphagocyte Theo yêu cầu.
TheNumberOne

1
Làm việc tốt với sự thay đổi của thẩm phán. Sao chép tập tin và lệnh riêng biệt là một ý tưởng tốt và ghi nhật ký lỗi là rất cần thiết.
Logic Knight

1
Nếu bạn có nghĩa là MAXTURNS, vui lòng thay đổi nó. Nó không phải là một phần của các quy tắc. Nó chỉ ngăn thẩm phán chạy mãi mãi (nhưng tôi đoán rằng các điều kiện chấm dứt ngăn chặn điều đó bằng mọi cách).
Hiệp sĩ logic

1
@githubphagocyte Đã sửa lỗi
TheNumberOne

1
Sau khi nhìn vào các trận chiến hoạt hình của bạn, tôi bắt đầu tự hỏi một trận chiến Swallower vs Swallower sẽ như thế nào. Người ta sẽ nhanh chóng gài bẫy người kia, hay sẽ là một cuộc chiến không ngừng cho sự thống trị của không gian?
Hiệp sĩ logic

6

Ngẫu nhiên, Ngôn ngữ = java, Điểm = 0,43012126100275

Chương trình này ngẫu nhiên đặt pixel trên màn hình. Một số (nếu không phải tất cả) các pixel sẽ không hợp lệ. Mặt khác, rất khó để thực hiện một chương trình nhanh hơn chương trình này.

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.File;
import java.io.InputStreamReader;

public class Random {

    static BufferedReader in = new BufferedReader(new InputStreamReader(System.in));

    static int n;

    static int height;

    static int width;

    public static void main(String[] args) throws Exception{
        BufferedImage image = ImageIO.read(new File(args[0]));
        height = image.getHeight();
        width = image.getWidth();
        n = Integer.parseInt(args[2]);
        while (true){
            String input = in.readLine();
            if (input.equals("exit")){
                return;
            }
            if (!input.equals("pick pixels")){
                continue;
            }
            for (int i = 0; i < n; i++){
                System.out.print((int) (Math.random() * width) + ",");
                System.out.print((int) (Math.random() * height) + " ");
            }
            System.out.println();
        }
    }
}

Đấu trường 1:

1

Đấu trường 2:

2

Đấu trường 3:

3

Đấu trường 4:

4


7
Tôi thấy bạn chưa rơi vào cái bẫy tối ưu hóa sớm .
Logic Knight
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.