package spoptim import ( "fmt" "math" "sort" "strconv" "encoding/json" "gem-spaas-coding-challenge/models" ) var powerplantFuel = map[string]string{"gasfired": "gas(euro/MWh)", "turbojet": "kerosine(euro/MWh)", "windturbine": "wind(%)"} func sliceSum(slice []float64) float64 { tot := 0.0 for _, el := range slice { tot += el } return tot } func ProductionPlanner(payload *models.Payload) []interface{} { // handle empty payload if payload == nil { return make([]interface{}, 0) } // format wind plants for consistency with other types of plants wind, _ := payload.Fuels.(map[string]interface{})["wind(%)"].(json.Number).Float64() for _, plant := range payload.Powerplants { plant_typed := plant.(map[string]interface{}) if plant_typed["type"] == "windturbine" { pmax, _ := plant_typed["pmax"].(json.Number).Float64() plant_typed["pmax"] = json.Number(strconv.FormatFloat((pmax * wind / 100), 'f', 20, 64)) } } payload.Fuels.(map[string]interface{})["wind(%)"] = json.Number(0) // order plants by merit sort.SliceStable(payload.Powerplants, func(i, j int) bool { planti, _ := payload.Powerplants[i].(map[string]interface{}) plantj, _ := payload.Powerplants[j].(map[string]interface{}) fuels, _ := payload.Fuels.(map[string]interface{}) fueli, _ := fuels[powerplantFuel[planti["type"].(string)]].(json.Number).Float64() fuelj, _ := fuels[powerplantFuel[plantj["type"].(string)]].(json.Number).Float64() effi, _ := planti["efficiency"].(json.Number).Float64() effj, _ := plantj["efficiency"].(json.Number).Float64() return fueli / effi < fuelj / effj }) // Naively assign production by merit-order (with one check // to replace the last plant if too big pmin) load := 0.0 cost := make([]float64, 0) res := make([]interface{}, 0) costToCompare := make([]interface{}, 0) resToCompare := make([][]interface{}, 0) for _, plant := range payload.Powerplants { plant_typed := plant.(map[string]interface{}) remainingLoad := *payload.Load - load if remainingLoad == 0 { break } pmin, _ := plant_typed["pmin"].(json.Number).Float64() pmax, _ := plant_typed["pmax"].(json.Number).Float64() eff, _ := plant_typed["efficiency"].(json.Number).Float64() fuel, _ := payload.Fuels.(map[string]interface{})[powerplantFuel[plant_typed["type"].(string)]].(json.Number).Float64() if pmin < remainingLoad { usedP := math.Min(remainingLoad, pmax) load += usedP cost = append(cost, usedP / eff * fuel) res = append(res, map[string]interface{}{ "name": plant_typed["name"].(string), "p": usedP, }) } else { altRes := make([]interface{}, 0) altCost := make([]float64, 0) copy(altRes, res) copy(altCost, cost) loadToRemove := pmin - remainingLoad for loadToRemove > 0 { if altRes[len(altRes)-1].(map[string]interface{})["p"].(float64) > loadToRemove { altCost[len(altCost)-1] = altCost[len(altCost)-1] * (1 - loadToRemove / altRes[len(altRes)-1].(map[string]interface{})["p"].(float64)) altRes[len(altRes)-1].(map[string]interface{})["p"] = altRes[len(altRes)-1].(map[string]interface{})["p"].(float64) - loadToRemove loadToRemove = 0 } else { loadToRemove = loadToRemove - altRes[len(altRes)-1].(map[string]interface{})["p"].(float64) altRes = altRes[:len(altRes)-1] altCost = altCost[:len(altRes)-1] } } altRes = append(altRes, map[string]interface{}{ "name": plant_typed["name"].(string), "p": pmax, }) altCost = append(altCost, pmax / eff * fuel) resToCompare = append(resToCompare, altRes) costToCompare = append(costToCompare, altCost) } } bestCost := sliceSum(cost) for i, altCost := range costToCompare { if sliceSum(altCost.([]float64)) < bestCost { bestCost = sliceSum(altCost.([]float64)) res = resToCompare[i] } } return res }