-func FillStateTable2(data planet_data, dims []int, table []State,
-fuel_remaining, edens_remaining int, planet string, barrier chan<- bool) {
- /* The dimension nesting order up to this point is important.
- * Beyond this point, it's not important.
- *
- * It is very important when iterating through the Hold dimension
- * to visit the null commodity (empty hold) first. Visiting the
- * null commodity represents selling. Visiting it first gets the
- * action order correct: arrive, sell, buy, leave. Visiting the
- * null commodity after another commodity would evaluate the action
- * sequence: arrive, buy, sell, leave. This is a useless action
- * sequence. Because we visit the null commodity first, we do not
- * consider these action sequences.
- */
- eden_capacity := data.Commodities["Eden Warp Units"].Limit
- addr := make([]int, len(dims))
- addr[Edens] = edens_remaining
- addr[Fuel] = fuel_remaining
- addr[Location] = data.p2i[planet]
- for addr[Hold] = 0; addr[Hold] < dims[Hold]; addr[Hold]++ {
- for addr[Cloaks] = 0; addr[Cloaks] < dims[Cloaks]; addr[Cloaks]++ {
- for addr[UnusedCargo] = 0;
- addr[UnusedCargo] < dims[UnusedCargo];
- addr[UnusedCargo]++ {
- if addr[Edens] + addr[Cloaks] + addr[UnusedCargo] <=
- eden_capacity + 1 {
- for addr[NeedFighters] = 0;
- addr[NeedFighters] < dims[NeedFighters];
- addr[NeedFighters]++ {
- for addr[NeedShields] = 0;
- addr[NeedShields] < dims[NeedShields];
- addr[NeedShields]++ {
- for addr[Visit] = 0;
- addr[Visit] < dims[Visit];
- addr[Visit]++ {
- FillStateCell(data, dims, table, addr)
- }
- }
+func CommodityIndex(data planet_data, name string) int {
+ index, ok := data.c2i[name]
+ if !ok {
+ panic("Unknown commodity " + name)
+ }
+ return index
+}
+
+func CreateStateTable(data planet_data, dims LogicalIndex) []State {
+ table := make([]State, StateTableSize(dims))
+ for i := range table {
+ table[i].value = VALUE_UNINITIALIZED
+ table[i].from = FROM_UNINITIALIZED
+ }
+
+ addr := make(LogicalIndex, NumDimensions)
+ addr[Fuel] = *fuel
+ addr[Edens] = *start_edens
+ addr[Location] = PlanetIndex(data, *start)
+ if *start_hold != "" {
+ addr[Hold] = CommodityIndex(data, *start_hold)
+ }
+ start_index := EncodeIndex(dims, addr)
+ table[start_index].value = Value(*funds)
+ table[start_index].from = FROM_ROOT
+
+ return table
+}
+
+/* CellValue fills in the one cell at address addr by looking at all
+ * the possible ways to reach this cell and selecting the best one. */
+
+func Consider(data planet_data, dims LogicalIndex, table []State, there LogicalIndex, value_difference int, best_value *Value, best_source LogicalIndex) {
+ there_value := CellValue(data, dims, table, there)
+ if value_difference < 0 && Value(-value_difference) > there_value {
+ /* Can't afford this transition */
+ return
+ }
+ possible_value := there_value + Value(value_difference)
+ if possible_value > *best_value {
+ *best_value = possible_value
+ copy(best_source, there)
+ }
+}
+
+var cell_filled_count int
+
+func CellValue(data planet_data, dims LogicalIndex, table []State, addr LogicalIndex) Value {
+ my_index := EncodeIndex(dims, addr)
+ if table[my_index].value == VALUE_BEING_EVALUATED {
+ panic("Circular dependency")
+ }
+ if table[my_index].value != VALUE_UNINITIALIZED {
+ return table[my_index].value
+ }
+ table[my_index].value = VALUE_BEING_EVALUATED
+
+ best_value := Value(VALUE_RUBISH)
+ best_source := make(LogicalIndex, NumDimensions)
+ other := make(LogicalIndex, NumDimensions)
+ copy(other, addr)
+ planet := data.i2p[addr[Location]]
+
+ /* Travel here */
+ if addr[Traded] == 0 { /* Can't have traded immediately after traveling. */
+ other[Traded] = 1 /* Travel from states that have done trading. */
+
+ /* Travel here via a 2-fuel unit jump */
+ if data.Planets[data.i2p[addr[Location]]].BeaconOn && addr[Fuel]+2 < dims[Fuel] {
+ other[Fuel] = addr[Fuel] + 2
+ hole_index := (dims[Fuel] - 1) - (addr[Fuel] + 2)
+ if hole_index >= len(flight_plan()) || addr[Location] != PlanetIndex(data, flight_plan()[hole_index]) {
+ for other[Location] = 0; other[Location] < dims[Location]; other[Location]++ {
+ Consider(data, dims, table, other, 0, &best_value, best_source)
+ }
+ }
+ other[Location] = addr[Location]
+ other[Fuel] = addr[Fuel]
+ }
+
+ /* Travel here via a 1-fuel unit jump (a hyper hole) */
+ if addr[Fuel]+1 < dims[Fuel] {
+ hole_index := (dims[Fuel] - 1) - (addr[Fuel] + 1)
+ if hole_index < len(flight_plan()) && addr[Location] == PlanetIndex(data, flight_plan()[hole_index]) {
+ other[Fuel] = addr[Fuel] + 1
+ for other[Location] = 0; other[Location] < dims[Location]; other[Location]++ {
+ Consider(data, dims, table, other, 0, &best_value, best_source)
+ }
+ other[Location] = addr[Location]
+ other[Fuel] = addr[Fuel]
+ }
+ }
+
+ /* Travel here via Eden Warp Unit */
+ if addr[Edens]+1 < dims[Edens] && (addr[Hold] == 0 || addr[UnusedCargo] > 0) {
+ _, available := data.Planets[data.i2p[addr[Location]]].RelativePrices["Eden Warp Units"]
+ if !available {
+ other[Edens] = addr[Edens] + 1
+ if other[Hold] != 0 {
+ other[UnusedCargo] = addr[UnusedCargo] - 1
+ }
+ for other[Location] = 0; other[Location] < dims[Location]; other[Location]++ {
+ Consider(data, dims, table, other, 0, &best_value, best_source)
+ }
+ other[Location] = addr[Location]
+ other[UnusedCargo] = addr[UnusedCargo]
+ other[Edens] = addr[Edens]
+ }
+ }
+ other[Traded] = addr[Traded]
+ }
+
+ /* Trade */
+ if addr[Traded] == 1 {
+ other[Traded] = 0
+
+ /* Consider not trading */
+ Consider(data, dims, table, other, 0, &best_value, best_source)
+
+ if !data.Planets[data.i2p[addr[Location]]].Private {
+
+ /* Sell */
+ if addr[Hold] == 0 && addr[UnusedCargo] == 0 {
+ for other[Hold] = 0; other[Hold] < dims[Hold]; other[Hold]++ {
+ commodity := data.i2c[other[Hold]]
+ if !data.Commodities[commodity].CanSell {
+ continue
+ }
+ relative_price, available := data.Planets[planet].RelativePrices[commodity]
+ if !available {
+ // TODO: Dump cargo
+ continue
+ }
+ base_price := data.Commodities[commodity].BasePrice
+ absolute_price := float64(base_price) * float64(relative_price) / 100.0
+ sell_price := int(absolute_price * 0.9)
+
+ for other[UnusedCargo] = 0; other[UnusedCargo] < dims[UnusedCargo]; other[UnusedCargo]++ {
+ quantity := *hold - (other[UnusedCargo] + other[Cloaks] + other[Edens])
+ sale_value := quantity * sell_price
+ Consider(data, dims, table, other, sale_value, &best_value, best_source)
+ }
+ }
+ other[UnusedCargo] = addr[UnusedCargo]
+ other[Hold] = addr[Hold]
+ }
+
+ /* Buy */
+ other[Traded] = addr[Traded] /* Buy after selling */
+ if addr[Hold] != 0 {
+ commodity := data.i2c[addr[Hold]]
+ if data.Commodities[commodity].CanSell {
+ relative_price, available := data.Planets[planet].RelativePrices[commodity]
+ if available {
+ base_price := data.Commodities[commodity].BasePrice
+ absolute_price := int(float64(base_price) * float64(relative_price) / 100.0)
+ quantity := *hold - (addr[UnusedCargo] + addr[Cloaks] + addr[Edens])
+ total_price := quantity * absolute_price
+ other[Hold] = 0
+ other[UnusedCargo] = 0
+ Consider(data, dims, table, other, -total_price, &best_value, best_source)
+ other[UnusedCargo] = addr[UnusedCargo]
+ other[Hold] = addr[Hold]