Click here to Skip to main content
15,868,016 members
Articles / Programming Languages / C#

A Stepwise C# Implementation of a Solution to the ‘Spiral Print’ Problem

Rate me:
Please Sign up or sign in to vote.
4.00/5 (2 votes)
4 May 20205 min read 6.3K   27   1   3
Stepwise implementation of a program to solve one version of the ‘spiral print’ problem
This article presents an implementation in C# of a solution to the problem of printing an array in spiral form.

Introduction

The statement of one version of the problem is usually made by means of an example. Given a 4 x 4 array such as:

ABCD
EFGH
IJKL
MNOP

write code to print the array clockwise in spiral form, that is, ABCDHLPONMIEFGKJ. The other version of the problem is to print it counterclockwise.

From the problem statement, it is clear that using two indices, say i and j, to access rows and columns, respectively, there must be a way to restrict their range as the printing progresses. Therefore, the following declarations are in order:

C#
const int n = 4;

static int minI = 0, maxI = n - 1;
static int minJ = 0, maxJ = n - 1;

where the static variables will constrain the range of values to be taken by the row and column indices i and j.

The printing process must continue until all the elements have been printed. Since the program would be expected to work for a square array of any size, the code would be embedded in an infinite loop, to be stopped when some termination condition is met. The first step involves printing the first row of the array, thus leading to the following initial coding of the program.

C#
using System;
using System.Diagnostics;

namespace StepwiseSpiralPrint
{
   class Program
   {
      const int n = 4;

      static int minI = 0, maxI = n - 1;
      static int minJ = 0, maxJ = n - 1;

      static char[ , ]
         arr = new char[ n, n ] { { 'A', 'B', 'C', 'D' },
                                  { 'E', 'F', 'G', 'H' },
                                  { 'I', 'J', 'K', 'L' },
                                  { 'M', 'N', 'O', 'P' } };

      static void Main( string[] args )
      {
         int i = minI, j = minJ;
         int stepI = 1, stepJ = 1; ;

         PrintArray( arr );
         Console.WriteLine( "Spiral print of the array:\n" );

         while ( true )
         {
            while ( j <= maxJ )
            {
               Console.Write( arr[ i, j ] );
               j += stepJ;
            }
            Debug.Assert( j > maxJ );

            break;
         }
         Console.WriteLine( "\n" );
      }// Main

      public static void PrintArray( char[ , ] arr )
      {
         int n = arr.GetLength( 0 );

         Console.WriteLine( "\nArray:\n" );

         for ( int i = 0; i < n; ++i )
         {
            for ( int j = 0; j < n; ++j )
            {
               Console.Write( "{0} ", arr[ i, j ] );
            }
            Console.WriteLine();
         }
         Console.WriteLine();
      }// PrintArray
   }// Program (class)
}// StepwiseSpiralPrint (namespace)

Variables stepI and stepJ in function Main are needed because index i can move downward or upward depending on the sign of stepI, while index j can move forward or backward depending on the sign of stepJ. Observe the use of System.Diagnostics.Debug.Assert, which allows the verification of the state of program variables. Function PrintArray naturally prints the array to be printed in spiral form, and it makes no assumptions about the dimensions of the array. Execution of the preceding program yields the following output:

Array:

A B C D
E F G H
I J K L
M N O P

Spiral print of the array:

ABCD

Press any key to continue . . .

From now on, only the changes to function Main will be shown. The next step involves printing the elements in the last column under the last element of the first row. That requires adjusting the value of index j and a while loop over index i, as follows:

C#
static void Main( string[] args )
{
   int i = minI, j = minJ;
   int stepI = 1, stepJ = 1; ;

   PrintArray( arr );
   Console.WriteLine( "Spiral print of the array:\n" );

   while ( true )
   {
      while ( j <= maxJ )
      {
         Console.Write( arr[ i, j ] );
         j += stepJ;
      }
      Debug.Assert( j > maxJ );

      --maxJ;
      --j;
      ++minI;

      i += stepI;
      while ( i <= maxI )
      {
         Console.Write( arr[ i, j ] );
         i += stepI;
      }
      Debug.Assert( i > maxI );

      break;
   }
   Console.WriteLine( "\n" );
}// Main

Observe that maxJ and minI have been adjusted in preparation for the next “round”. Execution of the modified Main function produces the following output:

Array:

A B C D
E F G H
I J K L
M N O P

Spiral print of the array:

ABCDHLP

Press any key to continue . . .

The next step requires printing the elements of the last row preceding the last element of the array. That involves adjusting the value of index i and changing the sign of stepJ so that index j moves backward.

C#
static void Main( string[] args )
{
   int i = minI, j = minJ;
   int stepI = 1, stepJ = 1; ;

   PrintArray( arr );
   Console.WriteLine( "Spiral print of the array:\n" );

   while ( true )
   {
      while ( j <= maxJ )
      {
         Console.Write( arr[ i, j ] );
         j += stepJ;
      }
      Debug.Assert( j > maxJ );

      --maxJ;
      --j;
      ++minI;

      i += stepI;
      while ( i <= maxI )
      {
         Console.Write( arr[ i, j ] );
         i += stepI;
      }
      Debug.Assert( i > maxI );

      --maxI;
      --i;

      Debug.Assert( i > maxI && j > maxJ );

      stepJ = -1;

      j += stepJ;
      while ( minJ <= j )
      {
         Console.Write( arr[ i, j ] );
         j += stepJ;
      }
      Debug.Assert( j == minJ - 1 );

      break;
   }
   Console.WriteLine( "\n" );
}// Main

Observe, again, that the value of maxI has been adjusted in preparation for the next round. Execution of the modified Main function yields the following output:

Array:

A B C D
E F G H
I J K L
M N O P

Spiral print of the array:

ABCDHLPONM

Press any key to continue . . .

The last step in the first round involves printing the elements in the first column between the first element of the last row and the first element of the array. To accomplish this, the value of index j must be adjusted, and the sign of stepI must be changed so that index i moves upward.

C#
static void Main( string[] args )
{
   int i = minI, j = minJ;
   int stepI = 1, stepJ = 1; ;

   PrintArray( arr );
   Console.WriteLine( "Spiral print of the array:\n" );

   while ( true )
   {
      while ( j <= maxJ )
      {
         Console.Write( arr[ i, j ] );
         j += stepJ;
      }
      Debug.Assert( j > maxJ );

      --maxJ;
      --j;
      ++minI;

      i += stepI;
      while ( i <= maxI )
      {
         Console.Write( arr[ i, j ] );
         i += stepI;
      }
      Debug.Assert( i > maxI );

      --maxI;
      --i;

      Debug.Assert( i > maxI && j > maxJ );

      stepJ = -1;

      j += stepJ;
      while ( minJ <= j )
      {
         Console.Write( arr[ i, j ] );
         j += stepJ;
      }
      Debug.Assert( j == minJ - 1 );

      ++j;
      stepI = -1;

      i += stepI;
      while ( minI <= i )
      {
         Console.Write( arr[ i, j ] );
         i += stepI;
      }
      Debug.Assert( i == minI - 1 );

      break;
   }
   Console.WriteLine( "\n" );
}// Main

As can be seen from the following output obtained by executing the program, the four steps implemented so far have printed the “outer layer” of the spiral.

Array:

A B C D
E F G H
I J K L
M N O P

Spiral print of the array:

ABCDHLPONMIE

Press any key to continue . . .

Now, before starting the next round, the conditions for terminating the infinite loop must be evaluated. Termination must occur when the “center” of the spiral has been reached.

C#
static void Main( string[] args )
{
   int i = minI, j = minJ;
   int stepI = 1, stepJ = 1; ;

   PrintArray( arr );
   Console.WriteLine( "Spiral print of the array:\n" );

   while ( true )
   {
      while ( j <= maxJ )
      {
         Console.Write( arr[ i, j ] );
         j += stepJ;
      }
      Debug.Assert( j > maxJ );

      --maxJ;
      --j;
      ++minI;

      i += stepI;
      while ( i <= maxI )
      {
         Console.Write( arr[ i, j ] );
         i += stepI;
      }
      Debug.Assert( i > maxI );

      --maxI;
      --i;

      Debug.Assert( i > maxI && j > maxJ );

      stepJ = -1;

      j += stepJ;
      while ( minJ <= j )
      {
         Console.Write( arr[ i, j ] );
         j += stepJ;
      }
      Debug.Assert( j == minJ - 1 );

      ++j;
      stepI = -1;

      i += stepI;
      while ( minI <= i )
      {
         Console.Write( arr[ i, j ] );
         i += stepI;
      }
      Debug.Assert( i == minI - 1 );

      ++i;
      ++minJ;

      if ( minI > maxI || minJ > maxJ )
      {
         break;
      }
      else
      {
         i = minI; j = minJ; stepI = 1; stepJ = 1;
      }
   }
   Console.WriteLine( "\n" );
}// Main

After the last call to Debug.Assert in function Main, index i and the limit minJ are adjusted in preparation for the next round (if any). The condition for termination occurs when either one of the min limits exceeding its corresponding max limit. If the condition is not satisfied, the indices are set to their lower limits, the step variables are re-initialized, and execution of the main while loop continues. Since there are no more forced break statements, the execution of the completed Main function produces the desired output.

Array:

A B C D
E F G H
I J K L
M N O P

Spiral print of the array:

ABCDHLPONMIEFGKJ

Press any key to continue . . .

Extending the Program

Now that a solution to the problem of clockwise-printing a square array has been obtained, a question arises: what if it is desired to print a non-square array? Furthermore, what if it is also desired to process more than one array? It turns out that, in answer to the first question, the code that was developed for a square array can handle arbitrary two-dimensional arrays without any changes, except for the definition of suitable constants for the two dimensions. In answer to the second question, the code in function Main above can be re-factored into a function, say SpiralPrintArray, which would take as an argument the array to be printed, and which would call a function (InitLimits) to initialize the min and max limits before performing its task. Without further ado, the complete program to handle arbitrary two-dimensional arrays is as follows:

C#
using System;
using System.Diagnostics;

namespace SpiralPrint
{
   class Program
   {
      const int n1 = 4, m1 = 4,
                n2 = 4, m2 = 5,
                n3 = 6, m3 = 5;
      
      static int minI, maxI;
      static int minJ, maxJ;

      static char[ , ]
         arr1 = new char[ n1, m1 ] { { 'A', 'B', 'C', 'D' },
                                     { 'E', 'F', 'G', 'H' },
                                     { 'I', 'J', 'K', 'L' },
                                     { 'M', 'N', 'O', 'P' } },
         arr2 = new char[ n2, m2 ] { { 'A', 'B', 'C', 'D', '1' },
                                     { 'E', 'F', 'G', 'H', '2' },
                                     { 'I', 'J', 'K', 'L', '3' },
                                     { 'M', 'N', 'O', 'P', '4' } },
         arr3 = new char[ n3, m3 ] { { 'A', 'B', 'C', 'D', '1' },
                                     { 'E', 'F', 'G', 'H', '2' },
                                     { 'I', 'J', 'K', 'L', '3' },
                                     { 'M', 'N', 'O', 'P', '4' },
                                     { 'u', 'v', 'w', 'x', 'y' },
                                     { '$', '%', '*', '#', '@' } };

      static void Main( string[] args )
      {
         SpiralPrintArray( arr1 );
         SpiralPrintArray( arr2 );
         SpiralPrintArray( arr3 );
      }// Main

      public static void SpiralPrintArray( char[ , ] arr )
      {
         InitLimits( arr.GetLength( 0 ), arr.GetLength( 1 ) );

         int i = minI, j = minJ;
         int stepI = 1, stepJ = 1;

         PrintArray( arr );
         Console.WriteLine( "Spiral print of the array:\n" );

         while ( true )
         {
            while ( j <= maxJ )
            {
               Console.Write( arr[ i, j ] );
               j += stepJ;
            }
            Debug.Assert( j > maxJ );

            --maxJ;
            --j;
            ++minI;

            i += stepI;
            while ( i <= maxI )
            {
               Console.Write( arr[ i, j ] );
               i += stepI;
            }
            Debug.Assert( i > maxI );

            --maxI;
            --i;

            Debug.Assert( i > maxI && j > maxJ );

            stepJ = -1;

            j += stepJ;
            while ( minJ <= j )
            {
               Console.Write( arr[ i, j ] );
               j += stepJ;
            }
            Debug.Assert( j == minJ - 1 );

            ++j;
            stepI = -1;

            i += stepI;
            while ( minI <= i )
            {
               Console.Write( arr[ i, j ] );
               i += stepI;
            }
            Debug.Assert( i == minI - 1 );

            ++i;
            ++minJ;

            if ( minI > maxI || minJ > maxJ )
            {
               break;
            }
            else
            {
               i = minI; j = minJ; stepI = 1; stepJ = 1;
            }
         }
         Console.WriteLine( "\n" );
      }// SpiralPrintArray

      public static void InitLimits( int _n, int _m )
      {
         minI = 0; maxI = _n - 1;
         minJ = 0; maxJ = _m - 1;
      }// InitLimits

      public static void PrintArray( char[ , ] arr )
      {
         int n = arr.GetLength( 0 ), m = arr.GetLength( 1 );

         Console.WriteLine( "\nArray:\n" );

         for ( int i = 0; i < n; ++i )
         {
            for ( int j = 0; j < m; ++j )
            {
               Console.Write( "{0} ", arr[ i, j ] );
            }
            Console.WriteLine();
         }
         Console.WriteLine();
      }// PrintArray
   }// Program (class)
}// SpiralPrint (namespace)

The execution of the program produces the following output:

Array:

A B C D
E F G H
I J K L
M N O P

Spiral print of the array:

ABCDHLPONMIEFGKJ

Array:

A B C D 1
E F G H 2
I J K L 3
M N O P 4

Spiral print of the array:

ABCD1234PONMIEFGHLKJ

Array:

A B C D 1
E F G H 2
I J K L 3
M N O P 4
u v w x y
$ % * # @

Spiral print of the array:

ABCD1234y@#*%$uMIEFGHLPxwvNJKO

Press any key to continue . . .

Usage of the Code

The ZIP file contains directories with all the versions (development steps) of the program. To use the code, create a Visual Studio console application and copy the code in the Program.cs file of your choosing. In the IDE, select Build->Build Solution. After the build succeeds, select Debug->Start Without Debugging. A Windows command-prompt window will appear, showing the output produced by the program.

Conclusion

This article has presented the stepwise implementation of a program to solve one version of the ‘spiral print’ problem. The code was written in the spirit of Niklaus Wirth’s principles set forth in his paper “Program Development by Stepwise Refinement” (Communications of the ACM, Vol. 14, Nr. 4, April 1971, pp. 221-227), and illustrates the power of such an approach to structured programming.

History

  • 3rd May, 2020: Initial version

License

This article, along with any associated source code and files, is licensed under The MIT License and Code Project Open License (CPOL).


Written By
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionI can't help but feel there must be a more eloquent solution to this Pin
Garth J Lancaster4-May-20 20:11
professionalGarth J Lancaster4-May-20 20:11 
AnswerRe: I can't help but feel there must be a more eloquent solution to this Pin
User 110609797-May-20 3:19
User 110609797-May-20 3:19 
AnswerRe: I can't help but feel there must be a more eloquent solution to this Pin
Stefan_Lang6-Jul-20 5:35
Stefan_Lang6-Jul-20 5:35 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.