package spoptim import ( "math" "sort" "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) } // cast the fuels to map and validate fuels, _ := payload.Fuels.(map[string]interface{}) for k := range powerplantFuel { if _, ok := fuels[powerplantFuel[k]]; !ok { res := make([]interface{}, 0) res = append(res, powerplantFuel[k] + " missing in Fuels") return res } } // format wind plants for consistency with other types of plants wind, _ := fuels["wind(%)"].(json.Number).Float64() for _, plant := range payload.Powerplants { if *plant.Type == "windturbine" { *plant.Pmax = math.Round(*plant.Pmax * wind / 10) / 10 } } fuels["wind(%)"] = json.Number(0) // order plants by merit sort.SliceStable(payload.Powerplants, func(i, j int) bool { fueli, _ := fuels[powerplantFuel[*payload.Powerplants[i].Type]].(json.Number).Float64() fuelj, _ := fuels[powerplantFuel[*payload.Powerplants[j].Type]].(json.Number).Float64() return fueli / *payload.Powerplants[i].Efficiency < fuelj / *payload.Powerplants[j].Efficiency }) // 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 { remainingLoad := *payload.Load - load if remainingLoad == 0 { break } fuel, _ := fuels[powerplantFuel[*plant.Type]].(json.Number).Float64() if *plant.Pmin < remainingLoad { if *plant.Pmax != 0 { usedP := math.Min(remainingLoad, *plant.Pmax) load += usedP cost = append(cost, usedP / *plant.Efficiency * fuel) res = append(res, map[string]interface{}{ "name": plant.Name, "p": usedP, }) } } else { altRes := make([]interface{}, len(res)) for i, re := range res { altRes[i] = map[string]interface{}{ "name": re.(map[string]interface{})["name"], "p": re.(map[string]interface{})["p"], } } altCost := make([]float64, len(cost)) copy(altCost, cost) loadToRemove := *plant.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.Name, "p": *plant.Pmin, }) altCost = append(altCost, *plant.Pmin / *plant.Efficiency * 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 }