DEV Community

Cover image for Let's Party with Stacks and Queues! Pt 3
Arit Developer
Arit Developer

Posted on

Let's Party with Stacks and Queues! Pt 3

In Part 2, we had a full-blown party going! But things were convenient; since we had a meal for every guest, everyone eventually got served. Now we'll address a likely scenario - a top meal that no guest wants:

my_party = Party.new(
    %w[vegetarian vegan keto normal paleo],
    %w[normal paleo vegan vegetarian])
Enter fullscreen mode Exit fullscreen mode

Console output:

Party Status:
 Meals: ["paleo", "normal", "keto", "vegan", "vegetarian"]
 Guests: ["normal", "paleo", "vegan", "vegetarian"]

No one was served the last round

Party Status:
 Meals: ["paleo", "normal", "keto", "vegan", "vegetarian"]
 Guests: ["paleo", "vegan", "vegetarian", "normal"]

Guest wanting a paleo meal has been served

Party Status:
 Meals: ["normal", "keto", "vegan", "vegetarian"]
 Guests: ["vegan", "vegetarian", "normal"]

No one was served the last round

Party Status:
 Meals: ["normal", "keto", "vegan", "vegetarian"]
 Guests: ["vegetarian", "normal", "vegan"]

No one was served the last round

Party Status:
 Meals: ["normal", "keto", "vegan", "vegetarian"]
 Guests: ["normal", "vegan", "vegetarian"]

Guest wanting a normal meal has been served

Party Status:
 Meals: ["keto", "vegan", "vegetarian"]
 Guests: ["vegan", "vegetarian"]

No one was served the last round

Party Status:
 Meals: ["keto", "vegan", "vegetarian"]
 Guests: ["vegetarian", "vegan"]

No one was served the last round

. . . . <hundreds of lines later> . . . . .

Party Status:
 Meals: ["keto", "normal", "vegetarian"]
 Guests: ["vegan", "vegetarian", "normal"]

No one was served the last round

Traceback (most recent call last):
    11913: from stacksandqueues2.rb:104:in `<main>'
    11912: from stacksandqueues2.rb:97:in `meal_service'
    11911: from stacksandqueues2.rb:91:in `move_queue'
    11910: from stacksandqueues2.rb:91:in `move_queue'
    11909: from stacksandqueues2.rb:91:in `move_queue'
    11908: from stacksandqueues2.rb:91:in `move_queue'
    11907: from stacksandqueues2.rb:91:in `move_queue'
    11906: from stacksandqueues2.rb:91:in `move_queue'
     ... 11901 levels...
        4: from stacksandqueues2.rb:91:in `move_queue'
        3: from stacksandqueues2.rb:82:in `move_queue'
        2: from stacksandqueues2.rb:74:in `view_party'
        1: from stacksandqueues2.rb:21:in `display_meals'
stacksandqueues2.rb:21:in `inspect': stack level too deep (SystemStackError)
Enter fullscreen mode Exit fullscreen mode

Welp! What happened? Well, we got an error because the queue keeps cycling round and round, aligning different guests with the "keto" meal, but there is no match. We need a way to trash this unwanted meal, but only after making sure that there's no guest with a keto-meal preference.

We know that a top meal is unwanted if the entire queue has cycled once. So we need to keep track of how many guests have rejected the top meal; when this number equals the number of guests in the queue, we know that no one wants our top meal.

Let's introduce a rejected counter as an argument for the move_queue method. This counter is initially set to zero. For each meal-guest mismatch, rejected is incremented by 1. Then rejected is compared to the queue size; if both values are equal, the top meal is "trashed" and rejected is reset to 0:

def move_queue(rejected=0)
    view_party

    if match?
      meals.take_meal
      served = guests.leave_queue
      puts "Guest wanting a #{served} meal has been served\n\n"
    else
      rejected += 1
      guests.rejoin_queue
      puts "No one was served the last round\n"
      puts "Top Meal Rejections: #{rejected}\n\n"

      if rejected == guests.size
        rejected = 0
        trashed_meal = meals.take_meal

        puts "Unwanted #{trashed_meal} meal has been trashed!\n\n"
      end
      move_queue(rejected)
    end
end
Enter fullscreen mode Exit fullscreen mode
Party Status:
 Meals: ["paleo", "keto", "normal", "vegetarian"]
 Guests: ["normal", "paleo", "vegan", "vegetarian"]

No one was served the last round
Top Meal Rejections: 1

Party Status:
 Meals: ["paleo", "keto", "normal", "vegetarian"]
 Guests: ["paleo", "vegan", "vegetarian", "normal"]

Guest wanting a paleo meal has been served

Party Status:
 Meals: ["keto", "normal", "vegetarian"]
 Guests: ["vegan", "vegetarian", "normal"]

No one was served the last round
Top Meal Rejections: 1

Party Status:
 Meals: ["keto", "normal", "vegetarian"]
 Guests: ["vegetarian", "normal", "vegan"]

No one was served the last round
Top Meal Rejections: 2

Party Status:
 Meals: ["keto", "normal", "vegetarian"]
 Guests: ["normal", "vegan", "vegetarian"]

No one was served the last round
Top Meal Rejections: 3

Unwanted keto meal has been trashed!

Party Status:
 Meals: ["normal", "vegetarian"]
 Guests: ["vegan", "vegetarian", "normal"]
Enter fullscreen mode Exit fullscreen mode

Awesome! Our unwanted keto meal has been trashed, and the meal service can continue unhindered - hopefully!

Now, yes, yes, I hear all you time-complexity aficionados:

"This rejected counter approach may work for small get-togethers, but what if you have 500 guests or more? Are we really going to cycle through them all to trash one meal?"

You're right - that would feel like forever and irritate our guests to no end. So how about we (the delightful party hosts) stand next to the meal stack, and once a first guest rejects a meal, we yell out to everyone: "HEY! DO ANY OF YOU WANT THIS TYPE OF MEAL?" If no one answers, then we trash it. Let's create the necessary methods:

# Add this method to the GuestQueue class:
def include?(meal)
    guests.include?(meal)
end

# Add this method to the Party class:
def does_any_guest_want(meal)
    guests.include?(meal)
end

# Modify the 'Party#move_queue' method to the following:
def move_queue
    view_party

    if match?
      meals.take_meal
      served = guests.leave_queue
      puts "Guest wanting a #{served} meal has been served\n\n"
    else
        puts "No one was served the last round\n"

        if does_any_guest_want(meals.top_meal)
            guests.rejoin_queue
        else
            trashed_meal = meals.take_meal
            puts "Unwanted #{trashed_meal} meal has been trashed!\n\n"
        end
        move_queue
    end
end
Enter fullscreen mode Exit fullscreen mode

Another scenario to explore is when the meals are gone but there are still guests waiting to be served. I'll leave that to you :) Like, perhaps a method that stacks additional meals to serve the remaining guests?


I truly hope that this tutorial has fleshed the concepts of Stacks and Queues out a little more! Please let me know what you think in the comments. Thank you so much for sticking with me this far.

Top comments (1)

Collapse
 
andrewbrown profile image
Andrew Brown πŸ‡¨πŸ‡¦

Nice read!