DEV Community

Discussion on: Daily Challenge #144 - Box Office Clerk

Collapse
 
avalander profile image
Avalander

Scala

object BoxOffice {
  sealed trait Answer
  object Yes extends Answer
  object No extends Answer

  type Register = Map[Int, Int]

  @tailrec
  def tickets (xs: Seq[Int], register: Register = Map()): Answer =
    xs match {
      case Nil => Yes
      case x :: rest =>
        x match {
          case 25  => tickets(rest, register add 25)
          case 50  =>
            if (register.hasItem(25)) tickets(rest, register remove 25 add 50)
            else No
          case 100 =>
            if (register.hasItem(50) && register.hasItem(25))
              tickets(rest, register remove 50 remove 25 add 100)
            else if (register.hasItem(25, 3))
              tickets(rest, register.remove(25, 3) add 100)
            else No
          case x   => throw new MatchError(s"Can't handle currency $x")
        }
    }

  implicit class ExtRegister(r: Register) {
    def hasItem (key: Int, amount: Int = 1): Boolean =
      (r get key) match {
        case None        => false
        case Some(value) => value >= amount
      }

    def add (item: Int, amount: Int = 1): Register = {
      val prev = r.getOrElse(item, 0)
      r.updated(item, prev + amount)
    }

    def remove(item: Int, amount: Int = 1): Register = {
      val prev = r.getOrElse(item, 0)
      r.updated(item, prev - amount)
    }
  }
}

And the tests:

class BoxOfficeTest extends FunSuite {
  test("tickets") {
    assert(tickets(List(25, 25, 50)) == Yes)
    assert(tickets(List(25, 100)) == No)
    assert(tickets(List(25, 25, 50, 50, 100)) == No)
    assert(tickets(List(25, 25, 50, 50)) == Yes)
    assert(tickets(List(25, 50, 25, 100)) == Yes)
  }
}