Library tutorials & articles

99 Bottles

The Implementation

I wanted to write something that actually took advantage of the language features. I made a separate Binge class that handles the output of the song. It's provided with a delegate to do the actual output, and it fires an event when there are no more bottles. It also accepts a name for the drink, other than beer, and a starting count. It even outputs "bottle" when the count is one!

The source to the class is below, and you can even watch it run!

/// Implementation of Ninety-Nine Bottles of Beer Song in C#.
/// What's neat is that .NET makes the Binge class a
/// full-fledged component that may be called from any other
/// .NET component.
///
/// Paul M. Parks
/// http://www.parkscomputing.com/
/// February 8, 2002
///

using System;

namespace NinetyNineBottles
{
   /// <summary>
   /// References the method of output.
   /// </summary>
   public delegate void Writer(string format, params object[] arg);

   /// <summary>
   /// References the corrective action to take when we run out.
   /// </summary>
   public delegate int MakeRun();

   /// <summary>
   /// The act of consuming all those beverages.
   /// </summary>
   public class Binge
   {
       /// <summary>
       /// What we'll be drinking.
       /// </summary>
       private string beverage;

       /// <summary>
       /// The starting count.
       /// </summary>
       private int count = 0;

       /// <summary>
       /// The manner in which the lyrics are output.
       /// </summary>
       private Writer Sing;

       /// <summary>
       /// What to do when it's all gone.
       /// </summary>
       private MakeRun RiskDUI;

       public event MakeRun OutOfBottles;


       /// <summary>
       /// Initializes the binge.
       /// </summary>
       /// <param name="count">How many we're consuming.</param>
       /// <param name="disasterWaitingToHappen">
       /// Our instructions, should we succeed.
       /// </param>
       /// <param name="writer">How our drinking song will be heard.</param>
       /// <param name="beverage">What to drink during this binge.</param>
       public Binge(string beverage, int count, Writer writer)
       {
           this.beverage = beverage;
           this.count = count;
           this.Sing = writer;
       }

       /// <summary>
       /// Let's get started.
       /// </summary>
       public void Start()
       {
           while (count > 0)
           {
               Sing(
                   @"
{0} bottle{1} of {2} on the wall,
{0} bottle{1} of {2}.
Take one down, pass it around,",
                   count, (count == 1) ? "" : "s", beverage);

               count--;

               if (count > 0)
               {
                   Sing("{0} bottle{1} of {2} on the wall.",
                       count, (count == 1) ? "" : "s", beverage);
               }
               else
               {
                   Sing("No more bottles of {0} on the wall.", beverage);
               }

           }

           Sing(
               @"
No more bottles of {0} on the wall,
No more bottles of {0}.", beverage);

           if (this.OutOfBottles != null)
           {
               count = this.OutOfBottles();
               Sing("{0} bottles of {1} on the wall.", count, beverage);
           }
           else
           {
               Sing("First we weep, then we sleep.");
               Sing("No more bottles of {0} on the wall.", beverage);
           }
       }
   }

   /// <summary>
   /// The song remains the same.
   /// </summary>
   class SingTheSong
   {
       /// <summary>
       /// Any other number would be strange.
       /// </summary>
       const int bottleCount = 99;

       /// <summary>
       /// The entry point. Sets the parameters of the Binge and starts it.
       /// </summary>
       /// <param name="args">unused</param>
       static void Main(string[] args)
       {
           Binge binge =
              new Binge("beer", bottleCount, new Writer(Console.WriteLine));
           binge.OutOfBottles += new MakeRun(SevenEleven);
           binge.Start();
       }

       /// <summary>
       /// There's bound to be one nearby.
       /// </summary>
       /// <returns>Whatever would fit in the trunk.</returns>
       static int SevenEleven()
       {
           Console.WriteLine("Go to the store, get some more...");
           return bottleCount;
       }
   }
}

Comments

  1. 01 Jan 1999 at 00:00

    This thread is for discussions of 99 Bottles.

Leave a comment

Sign in or Join us (it's free).

AddThis

Related discussion

Related podcasts

  • Looking into the C# Crystal Ball with Charlie Calvert and Bill Wagner

    One of the most exciting announcements from PDC was the news about C# 4.0 and Visual Studio 2010. With all the excitement and discussion throughout the event about these new developer tools, we reached out to two experts in the fields. Charlie Calvert and Bill Wagner sat down with Keith and Woody...

Events coming up

  • Dec 6

    Developing AJAX Web Applications with Castle Monorail

    London, United Kingdom

    Monorail is the model-view-controller engine of the Castle Project, bringing many of the best ideas of Ruby on Rails to the .NET world. In this talk, David De Florinier and Gojko Adzic show how Monorail makes it easy to develop .NET based AJAX applications, and how to use the Castle Project to build Web 2.0 applications effectively. Come to this session if you are a .NET web developer. Everyone is welcome!