Civ4/integration question and practice for the golfers

For reasons not clear even to myself, I’m trying to predict the growth
and
production of a city in CivIV (c.f. CivFanatics
http://www.civfanatics.com
).

Each city has an integer population and a number of tiles (actually 20
but
reduced here to 10 to speed-up testing). Each population point (P) can
work
one tile in a given turn. Tiles have three attributes excitingly named
F, G and H. I am attempting to enumerate all interesting combinations
of worked tiles for any given city size. Output is in gnuplot format.

Here’s the code (fore!).

puts “Starting #{Time.now}”

numArray = (1…10).to_a

Generate tiles

tiles = numArray.inject([]) { |acc, node| acc + [[rand(4),rand(4),
rand(4)]]
}

Generate unique combinations for given size

File::open(“output.txt”, “w”) { |file|
file << “#P F G H\n”
numArray.inject([[]]) { |acc, node|
acc.inject([]) { |acc, injectedNodes| acc + [injectedNodes,
injectedNodes + [node]] }
}.collect { |c|
c.inject([0,0,0,0]) { |acc, node| [acc[0].succ, acc[1] +
tiles[node-1][0], acc[2] + tiles[node-1][1], acc[3] + tiles[node-1][2]]
}
}.uniq.sort.inject(1) { |acc, node|
file << “\n” if node[0] == acc
file << " " << node.join(" ") << “\n”
node[0] == acc ? acc.succ : acc
}
}

puts “Finished #{Time.now}”

My problem is ensuring that only efficient combinations are output. If
we
look at a doctored example…

#P A B C
0 0 0 0

1 0 0 0
1 2 0 0 ← This is not interesting,
1 2 0 3 ← when this is possible.
1 0 1 0 ← However this still is, even though A+B+C is less
than
the line above.

Can anyone suggest a nice golfed algorithm to get rid of the useless
combinations? Remember that any of the three resources might be deemed
most
important, e.g. 1 1 0 0 is still interesting when 1 0 9 9 exists.

When this bit gets nailed down, the next step is integrating
permutations of
combinations over time and solving for maximum G and/or H, given that
acquiring enough F will increase P. It’s probably best to assume no
micro-management, i.e. when a city grows, the combination chosen for the
new
size remains in place until the next growth.

Thanks for getting this far, all comments welcome.

Not sure if anyone’s reading this, but here’s where I’m at now.
Permutations are generated and selected for each resource.

Warning! Running time is sensitive to changes in this number.

NumberOfTilesPerCity = 6

puts “Starting #{Time.now}”

Generate tiles

NumArray = (1…NumberOfTilesPerCity).to_a
tiles = NumArray.inject([]) { |acc, node| acc + [[rand(4),rand(4),
rand(4)]] }

Calc food required to achieve a pop size

def inc(size)
10 * 2 ** size - 20 # Or similar
end

Given a total amount of food, calc current size

def currentSize(food)
s = 1
s += 1 while food > inc(s)
s
end

Generate combinations

Combinations = NumArray.inject([[]]) { |acc, node| # Combinations (ints)
acc.inject([]) { |acc, injectedNodes| acc + [injectedNodes,
injectedNodes + [node]] }
}.map { |c|
c.inject([0,2,1,2]) { |acc, node| # Combinations (tiles)
(0…2).inject([acc[0].succ]) { |i,n| i + [acc[n.succ] +
tiles[node-1][n]] }
}
}.uniq.sort.inject([]) { |acc, node| # Tranches
acc + [acc.size <= node[0] ? [node] : acc.pop + [node]]
}.each { |set|
set.delete_if { |test| # Prune
set.inject(false) { |acc, comp|
acc or (!test.equal?(comp) and ((1…3).inject(true) { |i,n| test[n]
<= comp[n] and i } and acc = true))
}
}
}

Generate permutations

class Array
def permute(i = 0, *a)
return [a] if i == size
self[i].map { |x| permute(i+1, *(a + [x])) }.inject([]) { |m,x| m + x
}
end
end
Permutations = Combinations.permute

Containers to store results

EmptyResult = [0,0,0,0]
bestResult = []
strings = “Pop,Food,Gold,Hammers”.split(’,’)
4.times { |x| bestResult << (EmptyResult.dup << strings[x]) }

Integrate across t

PermutationResults = Permutations.map { |p|
result = [1,0,0,0]
80.times { |t|
result = [currentSize(result[1])] + (1…3).to_a.inject([]) { |i,n|
i + [result[n] + p[result[0]][n]]
}
result[0] = NumArray.size if result[0] > NumArray.size # Cap size
}
result
}.inject(bestResult) { |acc, node|
ret = acc
(acc.size).times { |x| ret[x] = node + [ret[x][ret.size]] if node[x]

ret[x][x] }
ret
}

Write results to disk

File::open(“output.txt”, “w”) { |file|
file << “# Combinations\n”
Combinations.each { |set|
set.each { |comb|
file << " " << comb.join(" ") << “\n”
}
file << “\n”
}

file << "# Permutations\n"
Permutations.each { |pset|
	pset.each { |p|
		file << " " << p.join(" ") << "\n"
	}
	file << "\n"
}
file << "# Results\n"
PermutationResults.each { |res|
	file << " " << res.join(" ") << "\n"
}

}

puts “Finished #{Time.now}”