From dafa2aa46d5c5222488aecf539bc08fa0bf92281 Mon Sep 17 00:00:00 2001 From: patsonluk Date: Sat, 2 Dec 2023 12:11:28 -0800 Subject: [PATCH 1/3] Fixed first class only nego cheat --- .../main/scala/com/patson/model/Link.scala | 21 +++++++++++++++++++ .../app/controllers/NegotiationUtil.scala | 19 +++++++++++------ 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/airline-data/src/main/scala/com/patson/model/Link.scala b/airline-data/src/main/scala/com/patson/model/Link.scala index 8f46db2fc..27eeb78e8 100644 --- a/airline-data/src/main/scala/com/patson/model/Link.scala +++ b/airline-data/src/main/scala/com/patson/model/Link.scala @@ -142,6 +142,27 @@ case class Link(from : Airport, to : Airport, airline: Airline, price : LinkClas futureCapacity } + /** + * Future LinkValues based on configuration that can accommodate the max pax (ie single class configuration with lowest space multiplier). + * + * This is usually only used for negotiation calculations + * @return + */ + def futureMaxCapacity(): LinkClassValues = { + //need to do all these calculations so we an support class with a non 1 lowest space multiplier... + val lowestClass = LinkClass.values.sortBy(_.spaceMultiplier).head + var maxCapacity = LinkClassValues.getInstance() + assignedAirplanes.foreach { + case (airplane, assignment) => { + val maxCapacityPerFlight = LinkClassValues.getInstanceByMap(Map(lowestClass -> (airplane.model.capacity / lowestClass.spaceMultiplier).toInt)) + //not doing maxCapacityPerFlight * futureFrequency ...in case we allow mixed model in the future... + maxCapacity = maxCapacity + (maxCapacityPerFlight * assignment.frequency) + } + } + + maxCapacity + } + def futureFrequency() = { assignedAirplanes.values.map(_.frequency).sum } diff --git a/airline-web/app/controllers/NegotiationUtil.scala b/airline-web/app/controllers/NegotiationUtil.scala index 65ab99810..b83170fb9 100644 --- a/airline-web/app/controllers/NegotiationUtil.scala +++ b/airline-web/app/controllers/NegotiationUtil.scala @@ -96,15 +96,21 @@ object NegotiationUtil { val officeStaffCount : Int = baseOption.map(_.getOfficeStaffCapacity).getOrElse(0) val airlineLinksFromThisAirport = airlineLinks.filter(link => link.from.id == airport.id && (isNewLink || link.id != existingLinkOption.get.id)) - val currentOfficeStaffUsed = airlineLinksFromThisAirport.map(_.getFutureOfficeStaffRequired).sum - val newOfficeStaffRequired = newLink.getFutureOfficeStaffRequired + + /** + * Use futureMaxCapacity for calculating negotiation difficulty to avoid loophole of negotiating with first class and switch config + */ + val currentOfficeStaffUsed = airlineLinksFromThisAirport.map(link => link.getOfficeStaffRequired(link.from, link.to, link.futureFrequency(), link.futureMaxCapacity())).sum + val newOfficeStaffRequired : Int = { + newLink.getOfficeStaffRequired(newLink.from, newLink.to, newLink.futureFrequency(), newLink.futureMaxCapacity()) + } val newTotal = currentOfficeStaffUsed + newOfficeStaffRequired if (newTotal < officeStaffCount) { - requirements.append(NegotiationRequirement(STAFF_CAP, 0, s"Requires ${newOfficeStaffRequired} office staff, within your base capacity : ${newTotal} / ${officeStaffCount}")) + requirements.append(NegotiationRequirement(STAFF_CAP, 0, s"Requires office staff within your base capacity : ${officeStaffCount}")) } else { val requirement = (newTotal - officeStaffCount).toDouble / 10 - requirements.append(NegotiationRequirement(STAFF_CAP, requirement, s"Requires ${newOfficeStaffRequired} office staff, over your base capacity : ${newTotal} / ${officeStaffCount}")) + requirements.append(NegotiationRequirement(STAFF_CAP, requirement, s"Requires office staff close to or exceeding your current base capacity : ${officeStaffCount}")) } val mutualRelationship = CountrySource.getCountryMutualRelationship(newLink.from.countryCode, newLink.to.countryCode) @@ -154,10 +160,11 @@ object NegotiationUtil { case class FrequencyRestrictionByModel(threshold : Int, frequencyRestriction : Int) def getToAirportRequirements(airline : Airline, newLink : Link, existingLinkOption : Option[Link], airlineLinks : List[Link]) = { - val newCapacity : LinkClassValues = newLink.futureCapacity() + //use futureMaxCapacity as calculation, to avoid cheat of changing config after negotiation + val newCapacity = newLink.futureMaxCapacity() val newFrequency = newLink.futureFrequency() - val existingCapacity = existingLinkOption.map(_.futureCapacity()).getOrElse(LinkClassValues.getInstance()) + val existingCapacity = existingLinkOption.map(_.futureMaxCapacity()).getOrElse(LinkClassValues.getInstance()) val existingFrequency = existingLinkOption.map(_.futureFrequency()).getOrElse(0) val capacityDelta = normalizedCapacity(newCapacity - existingCapacity) From 3556f535a64982cb346af07d130ca5e13cc5ed72 Mon Sep 17 00:00:00 2001 From: patsonluk Date: Sat, 2 Dec 2023 12:55:34 -0800 Subject: [PATCH 2/3] not a complete fix...but at least it makes it less convenient to cheat --- airline-web/app/controllers/NegotiationUtil.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/airline-web/app/controllers/NegotiationUtil.scala b/airline-web/app/controllers/NegotiationUtil.scala index b83170fb9..18c90af46 100644 --- a/airline-web/app/controllers/NegotiationUtil.scala +++ b/airline-web/app/controllers/NegotiationUtil.scala @@ -97,10 +97,10 @@ object NegotiationUtil { val officeStaffCount : Int = baseOption.map(_.getOfficeStaffCapacity).getOrElse(0) val airlineLinksFromThisAirport = airlineLinks.filter(link => link.from.id == airport.id && (isNewLink || link.id != existingLinkOption.get.id)) + val currentOfficeStaffUsed = airlineLinksFromThisAirport.map(_.getFutureOfficeStaffRequired).sum /** * Use futureMaxCapacity for calculating negotiation difficulty to avoid loophole of negotiating with first class and switch config */ - val currentOfficeStaffUsed = airlineLinksFromThisAirport.map(link => link.getOfficeStaffRequired(link.from, link.to, link.futureFrequency(), link.futureMaxCapacity())).sum val newOfficeStaffRequired : Int = { newLink.getOfficeStaffRequired(newLink.from, newLink.to, newLink.futureFrequency(), newLink.futureMaxCapacity()) } From 3c1716fafe3c8cba56805640ddc4e3c04974a495 Mon Sep 17 00:00:00 2001 From: patsonluk Date: Sat, 2 Dec 2023 12:56:56 -0800 Subject: [PATCH 3/3] not a complete fix...but at least it makes it less convenient to cheat --- airline-web/app/controllers/NegotiationUtil.scala | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/airline-web/app/controllers/NegotiationUtil.scala b/airline-web/app/controllers/NegotiationUtil.scala index 18c90af46..4f507770c 100644 --- a/airline-web/app/controllers/NegotiationUtil.scala +++ b/airline-web/app/controllers/NegotiationUtil.scala @@ -96,7 +96,6 @@ object NegotiationUtil { val officeStaffCount : Int = baseOption.map(_.getOfficeStaffCapacity).getOrElse(0) val airlineLinksFromThisAirport = airlineLinks.filter(link => link.from.id == airport.id && (isNewLink || link.id != existingLinkOption.get.id)) - val currentOfficeStaffUsed = airlineLinksFromThisAirport.map(_.getFutureOfficeStaffRequired).sum /** * Use futureMaxCapacity for calculating negotiation difficulty to avoid loophole of negotiating with first class and switch config @@ -107,10 +106,10 @@ object NegotiationUtil { val newTotal = currentOfficeStaffUsed + newOfficeStaffRequired if (newTotal < officeStaffCount) { - requirements.append(NegotiationRequirement(STAFF_CAP, 0, s"Requires office staff within your base capacity : ${officeStaffCount}")) + requirements.append(NegotiationRequirement(STAFF_CAP, 0, s"Requires ${newOfficeStaffRequired} office staff, within your base capacity : ${newTotal} / ${officeStaffCount}")) } else { val requirement = (newTotal - officeStaffCount).toDouble / 10 - requirements.append(NegotiationRequirement(STAFF_CAP, requirement, s"Requires office staff close to or exceeding your current base capacity : ${officeStaffCount}")) + requirements.append(NegotiationRequirement(STAFF_CAP, requirement, s"Requires ${newOfficeStaffRequired} office staff, over your base capacity : ${newTotal} / ${officeStaffCount}")) } val mutualRelationship = CountrySource.getCountryMutualRelationship(newLink.from.countryCode, newLink.to.countryCode)