家庭农民-Java
这五个家庭农民竭尽所能覆盖所有生产方案,与在该类别上工作的任何类别中能发挥最大作用的人一样。但是,在完成最初的任务后,家庭成员全都自行罢工。在初始分配后,他们不会互相勾结。我可能会在交易时让他们互相帮助。
FamilyFarmers.java
import java.io.IOException;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.Scanner;
public class FamilyFarmers {
final int MIN_PRODUCTION_CUTOFF = 4; // If my decision making has ended up
// with a family member producing
// less than this number, he will
// just produce his most productive
// item
final int NUMBER_PRODUCTS = 5;
final int MAX_TRADES = 50; // The number of trades per phase
final byte EOF = 04;
final byte NEW_LINE = 10;
final int BILLBOARD_SIZE = 1000;
boolean alive = true;
int[] myInventory;
int myNumber;
// Primarily, the line this instance of the program will be printing on in
// the billboard number 0 will be the "boss", and will do a bunch of the
// calculations (To avoid them being done multiple times)
MappedByteBuffer familyBillboard;
String myProduct; // What product (single string character) we will be
// making
Scanner stdin = new Scanner(System.in);
/**
* @param args
* A string in the form A-#,B-#,C-#,D-#,E-# representing the
* productivity of each good.
* @throws IOException
* @throws InterruptedException
* @throws UnexpectedPhaseTokenException
*/
public static void main(String[] args) throws IOException, InterruptedException {
new FamilyFarmers();
}
public FamilyFarmers() throws IOException, InterruptedException {
Runtime.getRuntime().addShutdownHook(new Thread() {
public void run() {
familyBillboard.clear();
familyBillboard.put(new byte[familyBillboard.limit()]);
Runtime.getRuntime().halt(0);
}
});
initialSetup();
mainLoop();
}
protected void mainLoop() throws InterruptedException {
int tradeCounter = 0; // 50 trades per phase
String currentStage;
int toTrade = -1;
int toGet = -1;
boolean purchase = false;
while (alive) {
System.out.println("T");
currentStage = stdin.nextLine();
if (currentStage.equals("P")) { // Production period
System.out.println(myProduct);
/*
System.out.println("G");
String currentInv = stdin.nextLine();
myInventory = parseProducts(currentInv);
toTrade = getMostProduct();
toGet = getLeastProduct();
*/
tradeCounter = 0;
} else if (currentStage.equals("M")) { // Market
System.out.println("G");
String currentInv = stdin.nextLine();
myInventory = parseProducts(currentInv);
tradeCounter++;
purchase = (Math.random() >= 0.5);
toTrade = getMostProduct();
toGet = getLeastProduct();
// If my goods are fairly even, it's time to head home!
if (myInventory[toTrade] - myInventory[toGet] <= 2) {
System.out.println("L");
continue;
}
// If I don't have much to trade...
if (toTrade <= 6) {
// But my goods levels are fairly even...
if (toGet >= 4) {
// I'll just leave the market
System.out.println("L");
}
}
if (purchase) {
System.out.println("P");
} else {
System.out.println("S");
}
} else if (currentStage.equals("T")) {
String toSend = "";
if (purchase) { // Buying
boolean finished = false;
String offer = stdin.nextLine();
offer += "," + stdin.nextLine();
String[] offers = parseOffer(offer);
int quantityOffered = Integer.parseInt(offers[0].split("-")[0]);
int productOffered = offers[0].split("-")[1].charAt(0) - 65;
// This loop will probably never get off the first
// iteration...
// Go through the offers, blindly pick the first one that
// looks good.
for (int index = 1; index < offers.length && !finished; index++) {
int quantityDesired = Integer.parseInt(offers[index].split("-")[0]);
int productDesired = offers[index].split("-")[1].charAt(0) - 65;
// If the request would leave me with less than two, I'm
// not interested
if (quantityDesired - (myInventory[productDesired]) > 2) {
// Too rich for my blood!
continue;
}
if (productDesired == toGet) {
// I'm not interested in trading what I'm trying to
// get!
}
if (productOffered == toGet) {
// Since this is what I want to trade for, I'll be
// willing to consider different offers than
// otherwise
if (quantityDesired <= quantityOffered * 1.25
&& myInventory[productDesired] - quantityDesired > 4) {
System.out.println((char) (productDesired + 65));
finished = true;
}
// If I would otherwise die without the product,
// I'll accept a really bad trade
// (Remember that the incoming offers are already
// sorted least to highest)
if (myInventory[toGet] < 2 && tradeCounter > MAX_TRADES / 2) {
System.out.println((char) (productDesired + 65));
finished = true;
}
}
// If the product is what I'm trying to trade, and the
// offer isn't too bad
if (productDesired == toTrade && quantityOffered * 1.25 <= quantityDesired) {
System.out.println((char) (productDesired + 65));
finished = true;
}
// If I am offered either as much as or more of
// something, I'll do it.
if (quantityOffered >= quantityDesired) {
System.out.println((char) (productDesired + 65));
finished = true;
}
}
if (!finished) {
// If we get this far, nothing struck my fancy
System.out.println("X");
}
} else { // Selling
int[] toBuy = getSameProducts(toGet);
// Make some self-beneficial offers in the first few rounds.
if (tradeCounter <= 5) {
toSend = "" + ("2-" + ((char) (toTrade + 65)));
for (int index = 0; index < toBuy.length; index++) {
toSend += (",3-" + ((char) (toBuy[index] + 65)));
}
} else {
// Basic offer. Just offer 1:1 for what I want.
toSend = "" + ("2-" + ((char) (toTrade + 65)));
for (int index = 0; index < toBuy.length; index++) {
toSend += (",2-" + ((char) (toBuy[index] + 65)));
}
}
// If trading has been going for awhile and I would die the
// next turn, I frantically offer everything I have for what
// I need to survive one more turn. This is probably a
// terrible strategy!
if (myInventory[toGet] < 2 && tradeCounter > MAX_TRADES / 2) {
toSend += ("4-" + ((char) (toTrade + 65))) + ",2-" + ((char) (toGet + 65));
}
if (toSend.length() < 6) {
// I couldn't find enough to sell...
System.out.println(toSend + "," + toSend);
// That's safe, right?
break;
}
// Put the products I would accept on a line after the
// product I want to sell
String[] splitSend = toSend.split(",");
toSend = splitSend[0] + "\n";
boolean first = true; // Don't prepend a comma on the first string
for (int index = 1; index < splitSend.length; index++) {
if (!first){
toSend += ",";
}
toSend += splitSend[index];
first = false;
}
System.out.println(toSend);
}
} else if (currentStage.equals("S")) { // I was skipped! Darn it!
} else {
// AAK! I received a token I don't know what to do with! I must
// be dead...
alive = false;
}
}
}
/**
* Returns the offers, sorted from least product desired to most, with the
* product being offered at the first index
*
* @param offer
* @return String[] index 0 contains the product being offered, the
* following indicies are the desired products ordered from least to
* most
*/
protected String[] parseOffer(String offer) {
String[] splitOffers = offer.split(",");
// Sort. Just using selection sort. The first index contains the string
// with the product being asked for,
// so should not be sorted.
for (int index = 1; index < splitOffers.length; index++) {
int indexOfMin = index;
int minimum = Integer.parseInt(splitOffers[index].split("-")[0]);
for (int jdex = index + 1; jdex < splitOffers.length; jdex++) {
int thisValue = Integer.parseInt(splitOffers[jdex].split("-")[0]);
if (thisValue < minimum) {
indexOfMin = jdex;
minimum = thisValue;
}
}
String temp = splitOffers[index];
splitOffers[index] = splitOffers[indexOfMin];
splitOffers[indexOfMin] = temp;
}
return splitOffers;
}
/**
* Returns an array of the indices of the product which I have the same
* quantity of in myInventory
*
* @param startingIndex
* - The index of a value to match
* @return
*/
protected int[] getSameProducts(int startingIndex) {
int[] toReturn = new int[0];
for (int index = startingIndex + 1; index < myInventory.length; index++) {
if (myInventory[index] == myInventory[startingIndex]) {
int[] temp = new int[toReturn.length + 1];
for (int jdex = 0; jdex < toReturn.length; jdex++) {
temp[jdex] = toReturn[jdex];
}
temp[temp.length - 1] = index;
toReturn = temp;
}
}
return toReturn;
}
/**
* Returns the index of the product which I have the least of in myInventory
* I can't help but feel that this lacks object-oriented design...
*
* @return
*/
protected int getLeastProduct() {
int toReturn = 0;
for (int index = 1; index < myInventory.length; index++) {
toReturn = myInventory[index] < myInventory[toReturn] ? index : toReturn;
}
return toReturn;
}
/**
* Returns the index of the product which I have the most of in myInventory
* I can't help but feel that this lacks object-oriented design...
*
* @return
*/
protected int getMostProduct() {
int toReturn = 0;
for (int index = 1; index < myInventory.length; index++) {
toReturn = myInventory[index] > myInventory[toReturn] ? index : toReturn;
}
return toReturn;
}
/**
* Returns an int[] containing the productivity of each product in
* alphabetical order
*
* @param products
* @return
*/
protected int[] parseProducts(String products) {
int[] toReturn;
// Split the string so that each line of the array has #-P
String[] lineProductivities = products.split(",");
// Split each string in the array so that it is just the number
for (int index = 0; index < lineProductivities.length; index++) {
lineProductivities[index] = lineProductivities[index].split("-")[0];
}
toReturn = new int[lineProductivities.length];
for (int index = 0; index < lineProductivities.length; index++) {
toReturn[index] = Integer.parseInt(lineProductivities[index]);
}
return toReturn;
}
/**
* Append my productivity string to the family billboard. If the file was
* empty when I got here (contained no newlines), I am the boss! The boss
* gives orders.
*
* @throws IOException
* @throws InterruptedException
*/
protected void initialSetup() throws IOException, InterruptedException {
String input;
myNumber = 0;
FileChannel familyBillboardFC;
Path billboardPath = FileSystems.getDefault().getPath("family_billboard.txt");
FileLock billboardLock;
byte[] argsByteArray;
byte currentByte = 0;
input = stdin.nextLine();
// Open the file and lock it
familyBillboardFC = FileChannel.open(billboardPath, StandardOpenOption.WRITE, StandardOpenOption.READ);
billboardLock = familyBillboardFC.lock();
// Map the contents of the file to a space in memory
familyBillboard = familyBillboardFC.map(FileChannel.MapMode.READ_WRITE, 0, BILLBOARD_SIZE);
// Convert the incoming string into an array of bytes
argsByteArray = input.getBytes();
for (int index = 0; index < BILLBOARD_SIZE; index++) {
currentByte = familyBillboard.get();
if (currentByte == NEW_LINE) {
myNumber++;
familyBillboard.mark();
}
}
if (myNumber == 0) {
familyBillboard.position(0);
familyBillboard.mark();
}
familyBillboard.reset();
for (byte b : argsByteArray) {
familyBillboard.put(b);
}
familyBillboard.put(NEW_LINE);
familyBillboard.put(EOF);
billboardLock.release();
Thread.sleep(100); // Give other programs a chance to launch
// Boss needs to wait for awhile to make sure the others have finished
// writing...
// I don't have any idea how to do this in an intelligent fashion. It is
// *probably* safe to sleep for a few hundred milliseconds, but I'm not
// certain. Instead, I'll try to take out a new lock. If I succeed
// twice, the file must be finished!
int counter = 0;
while (myNumber == 0) {
billboardLock = familyBillboardFC.tryLock();
if (billboardLock != null) {
billboardLock.release();
counter++;
} else {
counter = 0;
Thread.sleep(10);
}
if (counter >= 2) {
giveOrders();
break;
}
}
byte foo = familyBillboard.get();
// Until the boss has written out the instructions, sleep
while (foo < 65) {
Thread.sleep(10);
familyBillboard.reset();
foo = familyBillboard.get();
}
familyBillboard.reset();
myProduct = String.valueOf((char) familyBillboard.get());
} // initialSetup()
/**
* Run by the boss. Tries to sort the family so that every product is
* covered and so that whoever can produce the most of a product is
* producing it. Writes the character code representing the product to
* produce to the first character of the relevant line in the family
* billboard.
*/
protected void giveOrders() {
final int MAX_LINE_LENGTH = 24;
int numberMembers = 0;
byte currentByte = 0;
Integer[][] productivities; // Table of member's productivities
char[] selections; // Who will make what. selections[#] = the production
// letter for member #
familyBillboard.position(0);
// I have seen the rules to these games change. It's easy for me to
// accommodate more (or less than) 5 instances now. It may not be easy
// later
while (currentByte != EOF) {
currentByte = familyBillboard.get();
if (currentByte == NEW_LINE) {
numberMembers++;
}
}
currentByte = 0;
familyBillboard.reset();
selections = new char[numberMembers];
productivities = new Integer[numberMembers][NUMBER_PRODUCTS];
for (int index = 0; index < numberMembers; index++) {
byte[] currentLineBytes = new byte[MAX_LINE_LENGTH];
String currentLine;
// Read the next line
for (int jdex = 0; jdex < currentLineBytes.length; jdex++) {
currentByte = familyBillboard.get();
if (currentByte == NEW_LINE) {
break;
}
currentLineBytes[jdex] = currentByte;
}
currentLine = new String(currentLineBytes);
currentByte = 0;
int[] lineProductivities = parseProducts(currentLine);
// Need to iterate to get the int[] to Integer[]
for (int jdex = 0; jdex < NUMBER_PRODUCTS; jdex++) {
productivities[index][jdex] = lineProductivities[jdex];
}
}
// If there are at least as many producers as products, select the most
// productive for each producer. If there are overlaps, move the smaller
// one to the second most productive and re-check for overlaps. If there
// are overlaps and the productivity is tied, compare the second highest
// and so on.
// TODO What if members > 5?
if (numberMembers <= NUMBER_PRODUCTS) {
int[] overlapResult;
for (int index = 0; index < selections.length; index++) {
selections[index] = (char) (maxInArray(productivities[index]) + 65);
// Can convert from a max value in productivities to a
// human-readable character by adding 65, since 0 -> A, 1 -> B,
// etc.
}
int counter = 0; // I imagine there is a possibility of this loop
// not terminating. I will use this counter to
// forcefully break it.
// While there is an overlap
while ((overlapResult = arrayHasOverlaps(selections)) != null && overlapResult[0] != -1) {
byte productIndex = (byte) (selections[overlapResult[0]] - 65);
// 0 through the number of production options, where A = 0, B =
// 1, etc.
if (productivities[overlapResult[0]][productIndex] > productivities[overlapResult[1]][productIndex]) {
int index = findNextHighestFromIndex(productivities[overlapResult[1]], productIndex);
selections[overlapResult[1]] = (char) (index + 65);
}
if (productivities[overlapResult[1]][productIndex] > productivities[overlapResult[0]][productIndex]) {
int index = findNextHighestFromIndex(productivities[overlapResult[0]], productIndex);
selections[overlapResult[0]] = (char) (index + 65);
}
// Things are beginning to get mega hairy
if (productivities[overlapResult[0]][productIndex] == productivities[overlapResult[1]][productIndex]) {
int index0 = findNextHighestFromIndex(productivities[overlapResult[0]], productIndex);
int index1 = findNextHighestFromIndex(productivities[overlapResult[1]], productIndex);
if (productivities[overlapResult[0]][index0] > productivities[overlapResult[1]][index1]) {
selections[overlapResult[0]] = (char) (index0 + 65);
} else {
// I can't be bothered to go any further with this... If
// they're tied here, then to heck with it!
selections[overlapResult[1]] = (char) (index1 + 65);
}
}
counter++;
if (counter > BILLBOARD_SIZE) {
break;
}
}
}
// Check for less than my minimum cutoff. If one is, set it to its max.
for (int index = 0; index < selections.length; index++) {
byte b = (byte) (selections[index] - 65);
if (productivities[index][b] < MIN_PRODUCTION_CUTOFF) {
selections[index] = (char) (maxInArray(productivities[index]) + 65);
}
}
// Write the product to produce to the correct line
familyBillboard.position(0);
familyBillboard.put((byte) selections[0]);
// If we find a newline, write the selected character to the next
// spot. Otherwise, read the next character
for (int index = 1; index < selections.length;) {
byte thisByte = familyBillboard.get();
if (thisByte == NEW_LINE) {
familyBillboard.put((byte) selections[index]);
index++;
}
}
}
/**
* Look through the array. Find an element that is either later in the array
* and <= the value at the incoming index and > the value at the toReturn
* index, or earlier in the array and < the value at the current index and >
* the value at toReturn. If we weren't able to set the new index (Maybe we
* are already at the max value) return the index of the largest value
*
* @param array
* the array to search in
* @param incomingIndex
* the index of the value to begin searching with
* @return an index as described
*/
protected int findNextHighestFromIndex(Integer[] array, int incomingIndex) {
int toReturn = incomingIndex;
int comparisonValue = -1; // The value at toReturn
int index = (incomingIndex + 1) % array.length;
for (int counter = 0; counter < array.length; counter++) {
if (index > incomingIndex && array[index] == array[incomingIndex]) {
// If we have found an equal value later in the array, return
// immediately. In the unlikely event everything is equal,
// don't just take the value at the bottom index!
return index;
}
if (index > incomingIndex && array[index] < array[incomingIndex] && array[index] > comparisonValue) {
toReturn = index;
comparisonValue = array[toReturn];
}
if (index < incomingIndex && array[index] < array[incomingIndex] && array[index] > comparisonValue) {
toReturn = index;
comparisonValue = array[toReturn];
}
index++;
index %= array.length; // How often do you get to use %= ?
}
if (comparisonValue == -1) {
// In the unlikely event we weren't able to set comparisonValue
// (maybe we are already at the minimum?)
toReturn = maxInArray(array);
// This will probably contribute to those endless loops I mentioned
// above!
}
return toReturn;
}
/**
* Checks the array for any two elements being the same. If two are, return
* the indices. If not, return {-1, -1}
*
* @param selections
* The array to examine
* @return Indices of the overlapping elements or {-1, -1}
*/
protected int[] arrayHasOverlaps(char[] selections) {
int[] toReturn = new int[] { -1, -1 };
for (int index = 0; index < selections.length - 1; index++) {
for (int jdex = index + 1; jdex < selections.length; jdex++) {
if (selections[index] == selections[jdex]) {
toReturn[0] = index;
toReturn[1] = jdex;
return toReturn;
}
}
}
return toReturn;
}
/**
* Returns the index of the max value of an array. In the case of a tie,
* returns the earliest index.
*
* @param array
* the array to read
* @return the index of the largest element in the array
*/
protected <T extends Comparable<T>> byte maxInArray(T[] array) {
byte currentMax = 0;
for (byte index = 0; index < array.length; index++) {
currentMax = array[index].compareTo(array[currentMax]) > 0 ? index : currentMax;
}
return currentMax;
}
}
command.txt
cd bots/family_farmer && java FamilyFarmers
可以用
javac FamilyFarmer.java
bots / family_farmer文件夹中还应该有另一个空白文件family_billboard.txt。