King of the Hill - Firefighters

27

7

It is a dry summer in the prairie. The four farmers in the area realize that they can corner the market on corn by burning their neighbors crops. But they need a strategy for doing so; that is where you come in.

Your task is to write a bot to tell the farmers what to burn. The goal is to finish the game with the largest area of unburnt land. The playing field is a 32x32 grid. Each cell may be one of the following:

. - Ground

@ - A bot

# - Ash

W - Wet ground

1,2,3,4,5, or 6 - Fire

Fire grows in intensity by 1 each turn. Once it is 3 or higher, it will set cells next to it (horizontally or vertically) on fire. After fire hits 6, it turns into ash.

On each turn, bots receive as STDIN the following: bot starting x, bot starting y, bot current x position, bot current y position, and the board, separated by newlines. An example:

8
22
6
24
................................
................................
................................
.....................1..........
....................1#1.........
...................1#363........
....................16@1........
.....................31.........
................................
.........1.1....................
........15#62...................
........15@2....................
.........32.....................
................................
................................
................................
................................
................................
................................
................................
................................
................................
....4.1.........................
.....1#3........................
.....@3.........................
.....W..........................
................................
................................
................................
................................
................................
................................

(in this instance you are the bot in the bottom left).

You must output three characters, with an optional newline, representing the following:

Move - one of L, R, U, D, or S (stay)

Action - one of B (burn), P (pour water) or X (do nothing)

Direction - one of L, R, U, D or S - controls which cell you perform the action on

Fire does not affect bots.

Turn order is as follows: All bots move; all bots perform actions; then environmental rules happen. If you pour water on the ground, it will be wet (W) for one turn. Fire will not spread to wet ground. If you pour water on wet ground, it will continue to be wet. If you pour water on fire, it turns back to regular ground. You cannot do anything to ash.

Rounds are run with 4 bots at a time. The round ends after 50 turns, or when one bot runs out of unburnt ground, whichever comes first. Your score is calculated as the number of ground or wet ground cells in the 9x9 square centered on where your bot started.

Here is an example bot; it picks all three letters randomly and generally ends up burning down its own fields.

RandomBurner:

#!/usr/bin/env python
import random
print random.choice('LRUDS')+random.choice('BPX')+random.choice('LRUDS')

Rules:

  • No filesystem access outside of your own folder.
  • You may write to files if you need to store persistent data between turns, but only up to a maximum of 1kb per bot
  • You may not overwrite anybody else's bot
  • If you output an invalid move, your bot will sit still. If you output an invalid action, your bot will do nothing.
  • Please stick to common languages that can be run on a OSX or Linux box.

Controller code can be found here.

Initial results:

Average of 15 rounds:
---------------------
81 Farmer
56 CautiousBot
42 GetOff
41 Visigoth
40 DontBurnMeBro
37 FireFighter
35 Pyro
11 Protector

Update: Added Farmer, CautiousBot, GetOff, FireFighter, and Pyro.

Skyler

Posted 2015-10-06T16:52:37.680

Reputation: 897

1The board doesn't wrap around at the edges, right? – Zgarb – 2015-10-06T17:10:54.783

1Right. If you try to move past the edge, you just stand still. – Skyler – 2015-10-06T17:19:19.103

I understand Move and Action, but what does Direction do? – recursive – 2015-10-06T17:42:04.310

1@recursive Direction is where you do the action. E.g. if you do SBR, you stay where you are and burn the cell to the right of you. – Skyler – 2015-10-06T17:48:41.567

4I don't understand one detail. What land is mine and what is yours? – kaine – 2015-10-06T18:50:40.280

3Your land is what was inside the 9x9 block area centered on where you started. All bots start the round at least 8 blocks from each other, so there is no overlap. – Skyler – 2015-10-06T18:52:52.183

How do you tell the type of land under the bots? – TheNumberOne – 2015-10-06T19:40:42.210

2It's not provided. If you want to record it somehow, that is an option. Sitting on a fire to hide it is a valid strategy. – Skyler – 2015-10-06T19:49:59.777

If your land is a 9 by 9 square, does the bot you control in the example have less land to start with than the others? – The_Basset_Hound – 2015-10-07T01:20:16.080

1No, because it's centered on your bot; four spaces in each direction. – Skyler – 2015-10-07T01:24:07.317

Whoops, thought you meant 9 in each direction. Thanks. – The_Basset_Hound – 2015-10-07T01:28:32.840

Is a PHP answer acceptable? – Kodos Johnson – 2015-10-07T05:21:39.060

Does fire stop propagating at some point? Or the chain reaction of setting fire to adjacent cells goes on forever? – Ioannes – 2015-10-07T08:52:30.003

1@Andrew, PHP is fine as long as it can be run from the command line. The chain reaction continues until the round ends (unless stopped by water or ash). – Skyler – 2015-10-07T14:42:54.687

I tried running the controller code but I got some weird output. The bot is outputting UBR (move up, burn right). It goes straight down as expected but the fire doesn't spread to the left until it reaches the bottom. I took a quick look at the code but couldn't see what is causing this. Is there a reason for it?

– Kodos Johnson – 2015-10-08T00:39:22.200

In the example, shouldn't the current position be (5,24)? – CommonGuy – 2015-10-08T06:20:21.507

1@Andrew Indeed. There was a bug in the controller which I fixed before I ran the last rounds, but forgot to update here - should be fixed now. – Skyler – 2015-10-08T12:34:38.197

@Skyler I'm having a little trouble getting the controller to work, every character is just standing still. Can you clarify how to run it/ what the file structure is? Thank you – thefistopher – 2015-10-08T17:00:45.997

Sure, the file structure is firefighters.py and bots/, with all the bots in bots/. Run with python firefighters.py bot1name bot2name bot3name bot4name. – Skyler – 2015-10-08T17:10:51.263

Would an answer written in Felix be OK? It runs well on Linux...

– kirbyfan64sos – 2015-10-08T17:31:31.133

@kirbyfan64sos Should be fine. I'd just need to update Java on my Linux box, because Protector doesn't run on anything below 1.8. – Skyler – 2015-10-08T18:33:31.627

What is the format of the tournament? – horns – 2015-10-19T19:19:33.343

I am currently running bots in matches of 4, such that every bot is in a match with every other bot at least three times. The total score for a bot is the average of its score over all rounds it played in. – Skyler – 2015-10-19T19:38:55.443

BTW, your controller code has some bugs in the environmental effects section, specifically in managing the spread of fire. Spreading fire can overwrite more intense fires, and sometimes fires just get stuck at 5. I rewrote some of the controller code in python3 for my personal testing, let me know if you'd like it. – horns – 2015-11-04T13:32:57.680

Yes, I would be interested in that. – Skyler – 2015-11-04T14:24:19.893

1link. I changed it to run on python3, be compatible with windows, and added builtin tournament if called with no parameters. – horns – 2015-11-04T19:42:49.073

Answers

5

Visigoth

Visigoth tries to burn its enemies to the ground. It hopes to do this before anyone else gets to its land.

Run: python visigoth.py

#!/usr/bin/python

''' Charge the enemy and burn them to the ground. '''

import sys

data = sys.stdin.readlines()

startx, starty, x, y = [int(i) for i in data[0:4]]
field = [list(i) for i in data[4:]]

otherbots = []
for i in range(32):
    for j in range(32):
        if field[i][j]=='@': #bot
            if i!=y and j!=x:
                otherbots.append([j,i])

min_bot = otherbots[0]
for bot in otherbots:
    if abs(bot[0]-x)+abs(bot[1]-y) < abs(min_bot[0]-x)+abs(min_bot[1]-y):
        min_bot = bot

dx = min_bot[0]-x
dy = min_bot[1]-y

if abs(dy)>abs(dx):
    if dy>0:
        move = 'U'
    else:
        move = 'D'
else:
    if dx>0:
        move = 'R'
    elif dx<0:
        move = 'L'
    else:
        move = 'S'

if max(abs(x-startx), abs(y-starty))>4:
    if 0<x<31 and 0<y<31:
        dirs = {'U':(-1,0), 'D':(1,0), 'L':(0,-1), 'R':(0,1)}
        for i in dirs:
            if field[dirs[i][0]][dirs[i][1]] in ('.', 'W'):
                action = 'B'+i
                break
        else:
            # No free land nearby, go out in a blaze of glory
            action = 'BS'
    else:
        action = 'BS'
else:
    # Don't set own field on fire
    action = 'XS'

print move+action

This is my first entry, constructive criticism is appreciated!

taixzo

Posted 2015-10-06T16:52:37.680

Reputation: 161

"Fire does not affect bots" – Conor O'Brien – 2015-10-07T01:05:36.867

Visigoth seems to rarely burn anything. – Skyler – 2015-10-07T02:09:55.857

Oh, just noticed I had min where I should have had max. I fixed it. – taixzo – 2015-10-07T02:21:51.723

@CᴏɴᴏʀO'Bʀɪᴇɴ It's not actually trying to burn the enemy bots, just assuming they will stay near their own base. It would be very weak against itself. – taixzo – 2015-10-07T17:46:43.443

Oh, I see. That makes more sense. – Conor O'Brien – 2015-10-07T18:01:11.833

4

Java, Protector

Attempts to surround his field with a fence of ash.

Edit: Improved logic a bit. Probably won't make a difference.

import java.awt.Point;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Scanner;

/**
 * Created 10/6/15
 *
 * @author TheNumberOne
 */
public class Protecter {

    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        Point start = new Point(in.nextInt(), in.nextInt());
        Point current = new Point(in.nextInt(), in.nextInt());
        in.nextLine();
        String output = "";
        char[][] board = new char[32][];
        for (int i = 0; i < 32; i++){
            board[i] = in.nextLine().toCharArray();
        }
        List<Point> danger = new ArrayList<>();
        List<Point> close = new ArrayList<>();
        List<Point> closeDanger = new ArrayList<>();
        List<Point> fence = new ArrayList<>();
        for (int i = 0; i < 32; i++){
            for (int j = 0; j < 32; j++){
                Point p = new Point(i, j);
                char c = board[j][i];
                if (Math.abs(p.x - start.x) < 10 && Math.abs(p.y - start.y) < 10){
                    if ((c + "").matches("\\d")){
                        danger.add(p);
                    }
                }
                if (distance(current, p) == 1){
                    close.add(p);
                }
                if ((Math.abs(p.x - start.x) == 10 || Math.abs(p.y - start.y) == 10) && !(c + "").matches("#|\\d|@")){
                    fence.add(p);
                }
            }
        }
        closeDanger = new ArrayList<>(danger);
        closeDanger.retainAll(close);
        danger.sort(Comparator.comparingInt(a -> distance(current, a) / (board[a.y][a.x] - '0')));
        if (danger.size() > 0){
            output += moveTo(current, danger.get(0));
        } else {
            fence.sort(Comparator.comparingInt(a -> distance(current, a)));
            if (fence.size() == 0){
                output += "S";
            } else {
                output += moveTo(current, fence.get(0));
            }
        }
        closeDanger.sort(Comparator.comparingInt(a -> board[a.y][a.x] - '0'));
        if (closeDanger.size() > 0){
            output += "P";
            output += moveTo(current, closeDanger.get(0));
        } else {
            if (danger.size() == 0) {
                List<Point> closeFence = new ArrayList<>(fence);
                closeFence.retainAll(close);
                if (closeFence.size() > 0) {
                    output += "B";
                    output += moveTo(current, closeFence.get(0));
                } else {
                    if (!fence.contains(current)){
                        output += "PS";
                    } else {
                        output += "BS";
                    }
                }
            } else {
                if (!fence.contains(current)){
                    output += "PS";
                } else {
                    output += "BS";
                }
            }
        }
        System.out.println(output);
    }

    private static String moveTo(Point from, Point to) {
        if (from.x > to.x){
            return "L";
        }
        if (from.x < to.x){
            return "R";
        }
        if (from.y > to.y){
            return "U";
        }
        if (from.y < to.y){
            return "D";
        }
        return "S";
    }

    private static int distance(Point p1, Point p2){
        return Math.abs(p1.x - p2.x) + Math.abs(p2.y - p1.y);
    }

}

Place in a file named Protector.java.

Compile with: javac Protector.java
Run with: java Protector

TheNumberOne

Posted 2015-10-06T16:52:37.680

Reputation: 10 855

First, had to rename to Protecter.java to get it compiled. But, when I run it, it throws a java.lang.ArrayIndexOutOfBoundsException at line 29. – Skyler – 2015-10-07T00:23:55.320

@Skyler fixed it :) – TheNumberOne – 2015-10-07T02:30:37.230

Thanks, I added it to the results. Protector doesn't always manage to put out the fires he starts. – Skyler – 2015-10-07T03:04:37.137

2Kind of relevant ;) – Fatalize – 2015-10-07T07:58:31.897

@Fatalize That was what I was taught to do if I was caught in a forest/desert fire :) – TheNumberOne – 2015-10-07T12:44:05.030

2

DontBurnMeBro

Another Python entry. Guaranteed not to be the first to die (I think).

#!/usr/bin/python

print "SPS"

taixzo

Posted 2015-10-06T16:52:37.680

Reputation: 161

4Based on the spec pouring water is P not W. – randomra – 2015-10-08T13:01:04.210

Whoops, thanks. – taixzo – 2015-10-14T14:17:55.333

2

Pyro, python

Pyro likes fire. Pyro loves fire. Pyro lives in fire. I'm thinking "Pyro from TF2". Pyro likes to burn things. He won't burn his own territory, but he will try to get out of it, using a simple "pathfinding" algorithm.

import sys
import random
inpu          = sys.stdin.readlines()
ox,oy,x,y     = [int(i) for i in inpu[0:4]]
board         = [list(i) for i in inpu[4:]]
adjacentcells = [[[board[y+b][x+c],b,c] for b in range(-1,2)] for c in range(-1,2)]
action        = ""
infield=max(abs(ox-x),abs(oy-y))<=9
# let's find out what Pyro will do!
if not infield: # Pyro won't burn what's in his field.
    for row in adjacentcells:
        for entry in row:
            cell,a,b=entry
            if(a!=b):   # Can't act on these cells.
                if cell==".":   # burn it!!!!!!
                    action = "B"
                    if(a==0):
                        direction = {-1:"L",1:"R"}[b]
                    else:
                        direction = {-1:"D",1:"U"}[a]
            if action: break;
        if action: break;
    # Pyro doesn't care where he goes, so long as
    # Pyro's not in the field of Pyro.
    move = random.choice("LRUDS")
else:   # Thought Pyro hates water, Pyro must protect SOMETHING.
    action    = "P"
    direction = "S"
    # get the direction towards the center
    # Pyro will move away from ox and oy to
    # towards the center, if in the field.
    # Pyro will do this by first going right/left,
    # then going up/down. (This behaviour is
    # removed when he leaves his field.)
    cx = cy = 16
    while max(abs(cx-x),abs(cy-y))<=9:
        cx = random.randint(0,31)
        cy = random.randint(0,31)
    if(cx-x>0): #is to the left of the center
        move = "R"
    elif(cx-x<0): #is to the right of the cenetr
        move = "L"
    elif(cy-y>0): # is above center
        move = "D"
    elif(cy-y<0): # is below center
        move = "U"
    else:   # is at center (something went wrong!)
        move   = "S"
        action = "B"
if not move:
    move = "S"
if not action:
    action = "B"
if not direction:
    direction = "S"
print(move+action+direction)


""" Here, have a face!
MMMMMMMMMMMMMMMMMMMMMMMdyo/:-.````.:+sdMMMMMMMMMMMMMMMM
MMMMMMMMMMMMMMMMMMMNs/....----...``````.omMMMMMMMMMMMMM
MMMMMMMMMMMMMMMMMh/``..-://+//:-..````````/mMMMMMMMMMMM
MMMMMMMMMMMMMMMm:````..-:////:--..``````````sMMMMMMMMMM
MMMMMMMMMMMMMMs```````....--....`````````````/NMMMMMMMM
MMMMMMMMMMMMM/````````````````````````````````:NMMMMMMM
MMMMMMMMMMMM/```......``````````````````.......+mMMMMMM
MMMMMMMMMMMo.:::::::::::-````````````.-:::::::::/+yNMMM
MMMMMMMMMNs:-..-------::::.`````````://:-------..-:/hMM
MMMMMMMMm+:..---:::::---:::.```````::::---:::::--.`-/sM
MMMMMMMN+:`.---:::/:/:---:::``````.:::.--:::/:/:--.`:/h
MMMMMMMh:-`---://o+///:---::.`````-::---://oo///:--..:+
MMMMMMMs:..--:/+/sss+//:-.::.`````-::.-://+osyo//:-.-::
MMMMMMMs:----///+sosso/:--::``````.::.-://+oosso+:-.::/
MMMMMMMd:::-:/++/osyso+/--::```````::-://+/osyso+/--::s
MMMMMMMMs:/::::+ooos+o+/:::.```````.::::/+ooos/o+/:/:+N
MMMMMMMMMs://:+osooo+o+/::.``.....``.:/:/osoos+o+//:+mM
MMMMMMMMMM--:/++ysyoo+/:-...........`.-:/+ssso++/:-yNMM
MMMMMMMMMMs``.-:://::-..`.....---....``..--::::-.`.MMMM
MNNMddhyys+-.```````````...--:::::-...````````````+MMMM
////.-:///:-..``````````.:///++o/o+/:/.```````````yMMMM
---.`.-:::--..`````````..+//+ooo+++///.```````````mMMMM
/.--``..--...``````````..--:/+oo+/:--..``````````.MMMMM
s --.```...````````````...--/+++++/-...````````.`/MMMMM
d .--```````````````````...-::::::-...````````.-/+MMMMM
M.`--`  ````````````````....------....````````.-/.dMMMM
M+ .-.  `````````````````.:::::::::::.`````````.-sMMMMM
Mm``--`  ``````````````-://///////////:-````````/MMMMMM
MM: --.  ````````````.://::--......-::///-``````yMMMMMM
MMd`.-.  ``````````.:/::-..-:::::::.``-::::.````NMMMMMM
MMM+.--`  `````````:::-.-::-.......-::-`--::```+MMMMMMM
MMMN---`  ````````.--..:-.```````````..-`.--.``dMMMMMMM
MMMMd--.` `````..`.-.`-````````````````.-`...`/MMMMMMMM
MMMMMNh+ss/oydNMd``.`.`````````````````````.`.NMMMMMMMM
MMMMMMMMMMMMMMMMMd.``.```````````````````````dMMMMMMMMM
MMMMMMMMMMMMMMMMMMm:```````````````````````-dMMMMMMMMMM
MMMMMMMMMMMMMMMMMMMMy.```````````````````.oNMMMMMMMMMMM
MMMMMMMMMMMMMMMMMMMMMNs-```````````````:sNMMMMMMMMMMMMM
MMMMMMMMMMMMMMMMMMMMMMMMmy+:-.````.:+ymMMMMMMMMMMMMMMMM
"""

Conor O'Brien

Posted 2015-10-06T16:52:37.680

Reputation: 36 228

2Not enough ambiguity as to the Pyro's gender in your description and comments. – cole – 2015-10-08T01:41:09.150

@Cole Oh darn. I forgot about that quirk. I'll edit in some ambiguity, for sure ;) – Conor O'Brien – 2015-10-08T03:16:47.337

Doesn't run, because there is nothing after the else statement in line 21. – Skyler – 2015-10-08T12:39:45.597

Now breaks at line 5 ('data' is not defined) – Skyler – 2015-10-08T12:55:33.283

@Skyler Fixed again. I'm so sorry. I did this without my python interpreter. – Conor O'Brien – 2015-10-08T13:03:52.353

Line 6: name 'a' is not defined – Skyler – 2015-10-08T13:55:08.280

@Skyler I fixed it, looked over the code. There should be no more errors. – Conor O'Brien – 2015-10-08T14:41:23.720

Let us continue this discussion in chat.

– Skyler – 2015-10-08T14:49:44.730

2

GetOff, Python

GetOff just wants to keep his land for himself, and he's not afraid to chase those damn bots all over his land, squirting them with his water gun until they leave. While its property is not being violated, it makes tries its best to make sure his land doesn't get burned.

#!/usr/bin/env python

import sys

fire = ['1','2','3','4','5','6']

move = ''
ad = ''

data = sys.stdin.readlines()

startx, starty, x, y = [int(i) for i in data[0:4]]
board = [list(i) for i in data[4:]]

top = starty-4
bottom = starty+5
right = startx+5
left = startx-4

bots = []
for i in range(32):
    for j in range(32):
        if board[i][j]=='@':
            if i != y and j != x:
                bots.append([j,i])

fires = []
for i in range(32):
    for j in range(32):
        if board[i][j] in fire: #fire
            fires.append([j,i])

for bot in bots:
    if left < bot[0] < right and top < bot[1] < bottom: # if there's a bot in the field
        if bot[0] > x:
            move = 'R'
        elif bot[0] < x:
            move = 'L'
        elif bot[1] > y:
            move = 'D'
        elif bot[1] < y:
            move = 'U'
        else:
            move = 'S'
    else:
        nearest_fire = []
        for f in fires:
            if left < f[0] < right and top < f[1] < bottom:
                if nearest_fire == []:
                    nearest_fire = f
                elif (f[0]-x)+(f[1]-y) < (nearest_fire[0]-x)+(nearest_fire[1]-y):
                    nearest_fire = f
        if nearest_fire == []:
            move = 'S'
        else:
            if nearest_fire[0] > x:
                move = 'R'
            elif nearest_fire[0] < x:
                move = 'L'
            elif nearest_fire[1] > y:
                move = 'D'
            elif nearest_fire[1] < y:
                move = 'U'
            else:
                move = 'S'

if board[x-1][y] in fire: # position immediately to the left
    ad = 'L'
elif board[x+1][y] in fire: # position immediately to the right
    ad = 'R'
elif board[x][y-1] in fire: # position immediately up
    ad = 'U'
elif board[x][y+1] in fire: # position immediately down
    ad = 'D'
else:
    ad = 'S'

print(move+'P'+ad)

The Beanstalk

Posted 2015-10-06T16:52:37.680

Reputation: 311

Does the a < b < c syntax work in Python? I thought that evaluates to (a < b) < c, which is either 1 < c or 0 < c. Correct me if I am wrong. (Found in first conditional of bot loop.) – Conor O'Brien – 2015-10-08T03:18:38.120

It's always worked for me, but I'm not sure if it does in every version of python... – The Beanstalk – 2015-10-08T03:25:06.927

@CᴏɴᴏʀO'Bʀɪᴇɴ believe it does, 1<3>2 evaluates to True on my machine (if it were to group them it would return false: 1>2 and 1<1 are the possibilities). – cole – 2015-10-08T03:26:58.460

@Cole Thanks. I was in JavaScript-thinking mode. Just tried it out on repl.it. More reasons why Python is beautiful. – Conor O'Brien – 2015-10-08T03:28:13.620

2

Farmer, Java

The farmer only cares about his crops. He constantly watches his field for possible fires or invaders.

import java.awt.Point;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Scanner;

public class Farmer {

    public static void main(String[] args) {
        //row == y
        //col == x
        Scanner in = new Scanner(System.in);
        Point start = new Point(in.nextInt(), in.nextInt());
        Point current = new Point(in.nextInt(), in.nextInt());
        in.nextLine();
        char[][] board = new char[32][];
        for (int row = 0; row < 32; row++){
            board[row] = in.nextLine().toCharArray();
        }
        final List<Point> firesInField = new ArrayList<>();
        final List<Point> enemiesInField = new ArrayList<>();
        for (int row = 0; row < 32; row++) {
            for (int col = 0; col < 32; col++) {
                Point p = new Point(col, row);
                if (!isInField(start, p))
                    continue;
                char c = board[row][col];
                if (isFire(c)) {
                    firesInField.add(p);
                } else if (c == '@' && col != current.x && row != current.y) {
                    enemiesInField.add(p);
                } 
            }
        }
        List<Point> destinations = firesInField.size() > 0 ? firesInField : enemiesInField;

        if (destinations.size() > 0) {
            //take short paths to eliminate more fires
            destinations.sort(Comparator
                    .comparingInt((Point p) -> distance(p, current))
                    .thenComparingInt(p -> -1 * (board[p.y][p.x] - '0')));
            Point dest = destinations.get(0);
            print(moveTo(current, dest), "P", moveTo(current, dest));
        }

        //TODO start fires if an enemy has more intact ground

        //walk back to center
        print(moveTo(current, start), "P", moveTo(current, start));
    }

    private static void print(String move, String action, String actionMove) {
        System.out.println(move + action + actionMove);
        System.exit(0);
    }

    private static boolean isInField(Point centerOfField, Point toTest) {
        //add one extra, to prevent fires that are very near
        return distance(centerOfField, toTest) <= 10 && Math.abs(centerOfField.x - toTest.x) <= 5 && Math.abs(centerOfField.y - toTest.y) <= 5;
    }

    private static boolean isFire(char c) {
        return c == '1' || c == '2' || c == '3' || c == '4' || c == '5' || c == '6';
    }

    private static String moveTo(Point from, Point to) {
        if (from.x > to.x){
            from.x--;
            return "L";
        }
        if (from.x < to.x){
            from.x++;
            return "R";
        }
        if (from.y > to.y){
            from.y--;
            return "U";
        }
        if (from.y < to.y){
            from.y++;
            return "D";
        }
        return "S";
    }

    private static int distance(Point p1, Point p2){
        return Math.abs(p1.x - p2.x) + Math.abs(p2.y - p1.y);
    }
}

CommonGuy

Posted 2015-10-06T16:52:37.680

Reputation: 4 684

1Small improvement, in isFire you could use Character.isDigit instead. – J Atkin – 2015-10-14T19:35:04.417

1

FireFighter, Java

Fights all fires.

import java.awt.Point;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Scanner;

/**
 * Created 10/7/15
 *
 * @author TheNumberOne
 */
public class FireFighter {

    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        Point start = new Point(in.nextInt(), in.nextInt());
        Point current = new Point(in.nextInt(), in.nextInt());
        in.nextLine();
        String output = "";
        char[][] board = new char[32][];
        for (int i = 0; i < 32; i++) {
            board[i] = in.nextLine().toCharArray();
        }

        List<Point> danger = new ArrayList<>();
        List<Point> close = new ArrayList<>();
        List<Point> closeDanger;
        for (int i = 0; i < 32; i++){
            for (int j = 0; j < 32; j++){
                Point p = new Point(i, j);
                char c = board[j][i];
                if ((c + "").matches("\\d")){
                    danger.add(p);
                }
                if (distance(current, p) == 1){
                    close.add(p);
                }
            }
        }
        closeDanger = new ArrayList<>(danger);
        closeDanger.retainAll(close);

        Comparator<Point> comparator = Comparator.comparingInt((Point a) -> board[a.y][a.x]).reversed();

        danger.sort(comparator);
        danger.sort(Comparator.comparingInt(a -> distance(start, a)));
        if (danger.size() > 0){
            output += moveTo(current, danger.get(0));
        } else {
            output += moveTo(current, start);
        }
        closeDanger.sort(comparator);
        if (closeDanger.size() > 0){
            output += "P" + moveTo(current, closeDanger.get(0));
        } else {
            output += "PS";
        }
        System.out.println(output);
    }

    private static String moveTo(Point from, Point to) {
        if (from.x > to.x){
            return "L";
        }
        if (from.x < to.x){
            return "R";
        }
        if (from.y > to.y){
            return "U";
        }
        if (from.y < to.y){
            return "D";
        }
        return "S";
    }

    private static int distance(Point p1, Point p2){
        return Math.abs(p1.x - p2.x) + Math.abs(p2.y - p1.y);
    }

}

TheNumberOne

Posted 2015-10-06T16:52:37.680

Reputation: 10 855

0

Keeper, Python 2

import sys

Out = ["S", "P", "S"]

StX = int(sys.stdin.readline()) - 1
StY = int(sys.stdin.readline()) - 1
NowX = int(sys.stdin.readline()) - 1
NowY = int(sys.stdin.readline()) - 1
Map = []
for i in range(32):
    Map.append(sys.stdin.readline())

Pos = [NowX, NowY]
Area = [[StX, StY]]
for x in range(StX-4, StX+4):
    for y in range(StY-4, StY+4):
        Area.append([x, y])

Fires = []
for y in range(32):
    for x in range(32):
        if Map[y][x] in "123456":
            Fires.append([x, y])

Danger = []
for Tile in Area:
    if Map[Tile[1]][Tile[0]] in "123456":
        Danger.append(Tile)

Distance = {}
i = -1
for Fire in Danger:
    i += 1
    Distance[(Pos[0] - Fire[0], Pos[1] - Fire[1])] = i

i = min(Distance)
Closest = Danger[Distance[i]]

if Closest[0] > Pos[0]:
    Out[0] = Out[2] = "R"
    Pos[0] += 1
if Closest[0] < Pos[0]:
    Out[0] = Out[2] = "L"
    Pos[0] -= 1
if Closest[0] == Pos[0]:
    if Closest[1] > Pos[1]:
        Out[0] = Out[2] = "D"
        Pos[1] += 1
    if Closest[1] < Pos[1]:
        Out[0] = Out[2] = "U"
        Pos[1] -= 1

if Closest[0] + 1 == Pos[0] and Closest[1] == Pos[1]:
    Out[2] = "L"
if Closest[0] - 1 == Pos[0] and Closest[1] == Pos[1]:
    Out[2] = "R"
if Closest[1] + 1 == Pos[1] and Closest[0] == Pos[0]:
    Out[2] = "U"
if Closest[1] - 1 == Pos[1] and Closest[0] == Pos[0]:
    Out[2] = "D"
if Closest[0] == Pos[0] and Closest[1] == Pos[1]:
    Out[2] = "S"


print "".join(Out)

Could be simplified, but I'm tired.

The Keeper tries to keep its field from harm. If a fire appears, it hurries over to it and puts it out as fast as it can.

I may add accommodation for incoming fires as well.

The_Basset_Hound

Posted 2015-10-06T16:52:37.680

Reputation: 1 566

Line 36: ValueError: min() arg is an empty sequence - it throws errors if nothing is on fire yet. – Skyler – 2015-10-14T16:45:02.800

@Skyler I'll fix in a bit, sorry. – The_Basset_Hound – 2015-10-14T17:20:12.447

0

CautiousBot, Node.js (ES5)

This bot goes out and tries to set fire to other bots' land. It even sits on top of the fire for 3 ticks to hide it! However, one can never be too cautious, so it always makes sure it is close enough to put out any fires on its own land.

Notes:

  • Uses a state file called state.json stored in its working directory, used to store information about other bots' initial positions and to determine how long to hide a started fire. This must be deleted once the round is over (eg, when some bot has won). Otherwise the bot will be confused on the next round. (Let me know if this breaks rules.)
  • Requires the split module.
#!/usr/bin/env node

// imports
var fs       = require("fs");
var splitmod = require("split");

// variables
var startX, startY, currentX, currentY, board = [], state = {};
var DEBUG = false;

// utility functions
function debug(){
    if(DEBUG) console.log.apply(console, arguments);
}

// calculates manhattan distance which is also the number of turns it will take to get somewhere
function manhattan(x1, y1, x2, y2){
    return Math.abs(x2 - x1) + Math.abs(y2 - y1);
}

// calculates chebyshev distance (mostly used for determining if a bot is within someone's plot)
function chebyshev(x1, y1, x2, y2){
    return Math.max(Math.abs(x2 - x1), Math.abs(y2 - y1));
}

// gets the board character at x, y
function get(x, y){
    return board[y][x];
}

function readState(){
    try {
        state = JSON.parse(fs.readFileSync('state.json').toString());
        debug("Opened state file");
    } catch(e){
        // it must be the first turn
        createState();
    }
}

function writeState(){
    fs.writeFileSync('state.json', JSON.stringify(state));
    debug("Wrote state file");
}

// finds out where all the other bots are
function getBotPositions(){
    var positions = [];
    for(var x = 0; x < 32; x++){
        for(var y = 0; y < 32; y++){
            if(get(x, y) == '@' && x != currentX && y != currentY){
                positions.push({x: x, y: y});
            }
        }
    }
    return positions;
}

function createState(){
    debug("Creating state");
    // take a loot at where other bots are to record their land locations
    var botLands = getBotPositions();
    state['botLands'] = botLands;

    state['turn'] = 0; // which turn is it?
    state['lastFireTurn'] = -999; // which turn was the last one where this bot set a fire?
}


// finds whether a plot of land (defined by its center) has fire on it
function isLandBurning(x, y){
    for(var dx = -4; dx < 5; dx++){
        for(var dy = -4; dy < 5; dy++){
            if(get(x + dx, y + dy).match(/[1-6]/) != null) return true;
        }
    }
    return false;
}

// finds the fire with the highest number (and therefore the one to put out first)
function findFire(x, y){
    var highestNum = 0;
    var fire = {x: x, y: y};
    for(var dx = -4; dx < 5; dx++){
        for(var dy = -4; dy < 5; dy++){
            if(get(x + dx, y + dy).match(/[1-6]/) != null){
                var num = parseInt(get(x + dx, y + dy));
                if(num > highestNum){
                    highestNum = num;
                    fire = {x: x + dx, y: y + dy};
                }
            }
        }
    }
    return fire;
}

// figures out where to go to get somewhere
function getDirection(x1, y1, x2, y2){
    var direction = 'S';
    var cycx = Math.abs(y2 - y1) / Math.abs(x2 - x1);

    if(cycx < 1){
        if(x2 > x1) direction = 'R';
        if(x2 < x1) direction = 'L';
    } else {
        if(y2 > y1) direction = 'D';
        if(y2 < y1) direction = 'U';
    }

    debug("Getting direction", x1, y1, x2, y2, "result", direction);

    return direction;
}

// read input
var dataCycle = 0;
process.stdin.pipe(splitmod()).on('data', function(line){
    switch(dataCycle){
    case 0:
        startX = parseInt(line);
        break;
    case 1:
        startY = parseInt(line);
        break;
    case 2:
        currentX = parseInt(line);
        break;
    case 3:
        currentY = parseInt(line);
        break;
    default:
        board.push(line);
    }

    dataCycle++;
}).on('end', function(){
    // main bot code
    readState();
    state['turn']++;
    debug("It is turn", state['turn']);

    // get bot positions
    var botPositions = getBotPositions();

    var action = {type:'X', direction:'S'};
    var move   = 'S';

    var isMyLandBurning = isLandBurning(startX, startY);
    if(isMyLandBurning){ // hurry over there ASAP!
        debug("Bot land is burning!");
        var pos = findFire(startX, startY);
        debug("Fire found at", pos);
        move = getDirection(currentX, currentY, pos.x, pos.y);
        // simulate the move and figure out if/where to dump the water
        var newX = currentX + (move == 'R') - (move == 'L');
        var newY = currentY + (move == 'D') - (move == 'U');
        if(chebyshev(newX, newY, pos.x, pos.y) < 5){
            // on its own land, start dropping water like a madman
            debug("Dropping water");
            action.type = 'P';

            // if it can put out the target fire, then do that
            if(manhattan(newX, newY, pos.x, pos.y) == 1) action.direction = getDirection(newX, newY, pos.x, pos.y);
            // it's not in range of the target fire, so use the time to put out other fires
            // if it's moving on top of a fire, put that out
            else if(get(newX, newY).match(/[1-6]/) != null) action.direction = 'S';
            // if there's a fire around it then put that out
            else if(get(newX + 1, newY).match(/[1-6]/) != null) action.direction = 'R';
            else if(get(newX - 1, newY).match(/[1-6]/) != null) action.direction = 'L';
            else if(get(newX, newY + 1).match(/[1-6]/) != null) action.direction = 'D';
            else if(get(newX, newY - 1).match(/[1-6]/) != null) action.direction = 'U';
            else action.direction = 'S';
        }
    } else {
        // are there any bots that could start a fire when this bot is 6+ tiles away?
        var headBack = false;
        for(var i = 0; i < botPositions.length; i++){
            var otherBot = botPositions[i];
            var dist = manhattan(otherBot.x, otherBot.y, startX, startY) - 4;
            var myDist = manhattan(currentX, currentY, startX, startY);
            if(dist + 6 < myDist){
                headBack = true;
                break;
            }
        }
        if(headBack){ // they're probably up to no good
            debug("Bots are dangerously close, heading back");
            move = getDirection(currentX, currentY, startX, startY);
        } else if(state['turn'] - state['lastFireTurn'] < 3) { // no bots near own plot, time to consider other options
            debug("Hiding fire");
            // sneakily hide the fire if one was set :)
        } else { // last option is to go find land to burn
            debug("Finding land to burn");
            var closestX = 999, closestY = 999;
            for(var i = 0; i < state.botLands.length; i++){
                var otherLand = state.botLands[i];
                if(!isLandBurning(otherLand.x, otherLand.y) && chebyshev(currentX, currentY, otherLand.x, otherLand.y) > 4){ // find someone to burn
                    // use [-3, 3] here because on the first turn, the bots could have moved before this bot had a chance to see them
                    // meaning that the [-3, 3] region is the only one that is guaranteed to be on their land
                    for(var dx = -3; dx < 4; dx++){
                        for(var dy = -3; dy < 4; dy++){
                            var type = get(otherLand.x + dx, otherLand.y + dy);
                            var distThere = manhattan(currentX, currentY, otherLand.x + dx, otherLand.y + dy);
                            var distShortest = manhattan(currentX, currentY, closestX, closestY);
                            // find normal land, or wet land that will dry by the time the bot gets to it
                            if((type == '.' || type == '@' || (type == 'W' && distThere > 1)) && distThere < distShortest){
                                closestX = otherLand.x + dx;
                                closestY = otherLand.y + dy;
                            }
                        }
                    }
                }
            }
            if(closestX != 999 && closestY != 999){ // land found; go there
                debug("Target acquired", closestX, closestY);
                debug("Burning land");
                move = getDirection(currentX, currentY, closestX, closestY);
                if(move == 'S'){ // is it on the land? If so, then burn it
                    action.type = 'B';
                    action.direction = 'S';
                    state['lastFireTurn'] = state['turn']; // record when the fire was set
                }
            } else { // everyone else's land already has a fire
                debug("Default action");
                // default to heading back; one can never be too safe!
                move = getDirection(currentX, currentY, startX, startY);
            }
        }
    }

    // save the state file
    writeState();
    // output the action
    console.log(move + action.type + action.direction);
});

DankMemes

Posted 2015-10-06T16:52:37.680

Reputation: 2 769

It throws an error at line 340: Error: Cannot find module 'split'. I am using Node.js v0.10.30. – Skyler – 2015-10-14T14:48:14.043

cd botdir npm install split for some reason node doesn't like it installed globally for me but you could try that too – DankMemes – 2015-10-14T15:43:52.940