#

Programming 1

Preface

Try to guess the correct order of the blocks in order to make the program below work (hint: try to deduce from the even parity of the parentheses and the indentation):

#

This study material is the so-called lecture notes for the course Programming 1. The phrase “lecture notes” refers to such literary material that introduces the topics of the course in approximately the same order and in approximately the same light as they are introduced on lectures. In order to keep the length of the lecture notes in check, topics are not introduced all-encompassingly at all. For this reason, a good book on the topic and an unbiased attitude towards searching for information on your own is recommended to support your learning. The most current information is of course found online - as long as you remember source criticism. Note also that most of the books that are available approach programming from the point of view of a certain programming language - especially those directed to beginners. This is somewhat natural, because people also need some common language to communicate with each other. For the same reason, learning to program would be quite difficult without learning the basics of one language first.

To make the structure clearer, most books usually cover one topic systematically from beginning to the end. However, when a child is learning how to speak, she is not able to adopt all of the grammatic aspects of a certain sentence structure all at once. Similarly, while learning the basics of programming, the student may not yet have the necessary understanding to be able to grasp all structures and possibilities. The order of discussing topics on these lecture notes and on the lectures is so that we first give examples, then discuss the necessity of these examples, and then explore the theoretical and practical aspects of the topic. Hence, these lecture notes stratch the surface of the basics of programming from one point of view. Books and online sources offer necessary advanced information.

The example language in these lecture notes is the C# language. The word example is stressed, because the structure and examples in the lecture notes could be almost identical for any other programming language. The most important thing on an introduction course to programming is to learn the programmer’s way of thinking. Changing the language to another language in the same language family is more like changing dialects in natural language rather than changing languages altogether. In other words, if you learn to program in one language, you can already read programs coded in other languages after a small amount of practice. Coding in another language may be more challenging, but the usually they contain the same constructs. Programming languages come and go, and you shouldn’t learn just one, but learn as many as you can. Programming courses equivalent to this one have been held in University of Jyväskylä with the following languages: Fortran, Pascal, C, C++, Java, and now C#. Some universities use Python, others use Scala as their beginner language. All of these in some sense belong in the same language family and abide mostly by the same principles even though the details may vary a lot from time to time.

It is impossible to learn how to program by just reading books. For this reason, the course also contains doing weekly exercises (demos), supervised computer lab work, and completing a project work. More information about these, as well getting and installing the tools used on the course can be found on the course home page:

These lecture notes are based on the Programming 1 lecture notes written in 2009 by Martti Hyvönen and Vesa Lappalainen, which has been the result of multiple different authors starting all the way from the 80’s. The largest contribution has been made by Timo Männikkö and Vesa Lappalainen.

Jyväskylä, 2.1.2013

Martti Hyvönen, Vesa Lappalainen, Antti-Jussi Lakanen

Epilogue of preface

The newest version of the lecture notes have been written in TIM (The Interactive Material) system. The idea behind TIM is that different things, such as programming, can be tried without installing any programs at all. This will hopefully lower the threshold to starting programming. Unfortunately the technology we use (the language and subroutine libraries) do not offer the possibility to make interactive games in TIM, so for more advanced programming we still need to install programming tools, in this Visual Studio and Jypeli. These tools will be discussed later in these lecture notes and in other materials for the course in more detail.

Special thanks to the ACOS Content Server project in the Aalto University for the algorithm visualisations in the material.

Jyväskylä, 29.8.2014 Vesa Lappalainen, Antti-Jussi Lakanen

Lecture notes translated in 2017 by MK.

17 Dec 17
#

0. Introduction

Even though the course is designed as a “game programming” course, 90% of its content is exactly the same as in any other programming course. If there is someone who does not want to make a game as one’s project work, it is of course possible to write any other kind of small program.

0.1 About the content and aims of the course

To get the general idea (in English) of the content of this course, watch the video about how to make the game GalaxyTrip in less than five minutes. While watching these videos, don’t be afraid of the fact that you can’t do the same (yet); Watch these videos in order to understand what you should learn and will learn during this course.

#

Video 1: GalaxyTrip less than 5 minutes, Demonstrated in SIGCSE11 symposium. Antti-Jussi Lakanen/Vesa Lappalainen

If you want to watch a longer (45 minutes) video about the same topic (in Finnish), watch this video:

#

Video 2: Galaksit räjähtää: Pelin tekeminen 45 minuutissa, Antti-Jussi Lakanen, Levels-tapahtuma 9.4.2011

These videos showcase what kinds of games have been made in courses before:

#

Video 3: Programming 1, spring 2014 project works

#

Video 4: Game programming course for youth (1 week), summer 2013

0.2 Aims of the course

At the beginning of the course, you are expected to be able to know how to use a computer. You should be familiar with at least the use of different (text) editors, keyboard shortcuts, and preferably the command prompt as well. These days, of course, the command prompt is (unfortunately) not very familiar to most, which is why it is recommended that you see the extra material page of this course or Paavo’s survival guide (Note: both of these are currently available only in Finnish; if you cannot understand Finnish, see for example An A-Z Index of the Windows CMD command line or search for English command prompt tutorials)

#
Check your understanding

Which of the following can you do in the command prompt? After answering, a very simple list of commands is given. Commands in Windows and commands in Linux and macOS are separated by a slash(/). More detailed instructions (in Finnish) in the links above.

You won’t need any previous programming experience.

During the course you should learn the following (the level of competence on an adapted Bloom taxonomy: 1=remember, 2=understand, 3=know how to apply, 4=know how to analyse, 5=know how to review, 6=know how to create)

Below move your skill forward during the course. First check Modify.

#

Please to interact with this component.

Osattava asia123456
The basic idea of structured programming o
Algorithmic thinking o
Basics of the C# language o
Sequence o
Variables o
Subprograms and functions o
Passing parametres o
Conditional statements o
Loops o
Arrays o
File handling within programs o
Use of objects o
Unit testing (TDD) o
Use of debugger o
Notations, ASCII code o
Recursion o
Reading and writing documentation o

Remember to view the video directory of the course as well.

0.3 TIM instructions

Instructions below are for TIM-version that is available from https://tim.jyu.fi/view/kurssit/tie/ohj1/moniste/Ohjelmointi-1/en. We recommend to use the TIM-version parallel to this material. For example the full program codes are visible only in TIM-version.

#
General TIM instructions (in Finnish) Lecture 1 (2m46s)

This TIM-environment-based material consists of different interactive parts. Videos were already introduced in the previous chapters. It is best to close the videos after viewing to save the capacity of your device.

This material contains links to other materials. These links are extra material and there is no need to follow them when going through the material for the first time. It is easy to get lost in a jungle of links.

It is best to be logged in (Login) at all times in order to keep track of your own progress. When you are logged in, you can see red bars on the right side of the material which indicate the parts that you haven’t marked as read yet. When you have read (and understood :-) ) a passage of text, click the red bar to remove it. This way you can easily see which parts you haven’t gone through yet. This is especially helpful if you jump from place to place in the material in different order than in which it was written. The bar may also be yellow when you have read a passage, but its contents have changed since you’ve read it. Click on the yellow bar after you have understood and internalised the changed passage.

The upper left corner of the page contains an image of a book or depending on the size of the screen/resolution a menu image which contains an image of a book. Click on the book image to open the table of contents. Click the image again to close the table of contents.

When you point the cursor to the left side of a paragraph, a blueish green bar appears. Clicking this bar opens a menu where you can find e.g. the Comment/Note button, which allows you to add notes related to the paragraph. Use this feature actively. You can add notes either to yourself or use a comment to point out that something in the paragraph is unclear or erroneous.
In case of an error in the paragraph, please provide a correction suggestion as well. Otherwise, use the “Everyone” choice with consideration, and add your personal notes with the choice “Just me”.

If you want to search for something, use the Find function of your browser (Ctrl-F in most browsers).

If you want to find a specific page again easily, make a TIM bookmark of it. You can add a bookmark by clicking the paper clip icon in the upper left corner of the page. Of course you can also add a regular bookmark in your browser, but the benefit of a TIM bookmark is that it works in any browser. Start by bookmarking this page. Click the paper clip icon and add the page for example under the heading Ohj1 with the name Material or Handout.

In the preceding videos, programs were coded in the Visual Studio IDE (Integrated Development Environment). TIM itself contains a small built-in environment, which can complete simple tasks, such as:

#

First, run the code by clicking the Aja ('Run') button. Then change the code so that the ball is red. Run the program again. Then make the background blue.

//
        Level.Background.Color = Color.Black;
        PhysicsObject ball = new PhysicsObject(200,200,Shape.Circle);
        ball.Color = Color.Yellow;
        Add(ball);

 

#
See instructions related to the task on video Lecture 1 (2m21s)

There is a link Show all code under the task box. By clicking this link you can view all of the code required to run this program. You can still edit the program, but you cannot insert any code in the “wrong” place. The same link also hides the “extra code”.

The link Highlight allows you to change the editor type so that it colours in the code according to the syntax of the used language and can fill in words that are familiar to the editor.

The link Reset allows you to “reset” your answer and start from template from the beginning. Try each link.

The task could continue by adding (before the line Add(ball)) the line:

       ball.Position = new Vector(150, 100);

Try this as well. So, copy/paste the line of code above to the longer code from earlier, above the line Add(ball). See what happens when you write the colour Red in lower case. Correct the colour back to capitalised Red and see what happens when you change the values in Vector.

You can also play around with this example coded in another language (VPython). Here, you can also change the point of view by holding the right mouse button and moving the mouse around.

#
ball=sphere(pos=vector(4,7,3),radius=2,color=color.green)
redbox=box(pos=vector(4,2,3),size=vector(8,4,6),color=color.red)

 

#

1. What is programming?

Programming is giving instructions.

What is instruction?

05 Oct 19
#

1.1 Algorithms and instructions

At its simplest, programming is giving instructions in order to perform a predefined task. There are many actions similar to programming in a person’s everyday life. An example of an algorithm is giving directions to someone by phone so that they can drive to a place formerly unfamiliar to them. We do this by giving a series of instructions and commands that direct the performance of this action. Nowadays a navigator reads directions one by one. Similarly, a program executes commands one by one, keeping track of the place where the execution currently is. An example of rudimentary programming is using a microwave oven; the oven is given instructions of how long it should operate and how much power to use.

Programming has many levels. Nowadays there are, for example, tractors where the farmer can program it to go through the fields in a certain pattern. For caution and in case of sharp turns, the farmer must still be inside the tractor to make sure that everything goes well. In a certain way, the farmer must also know how to program. But before we got to this point, it required a vast amount of engineering work and programming to make sure that the GPS satellites, error correction and the programming in the tractor’s computer got to the level where it is easy for the farmers themselves to program.

In Suonenjoki, strawberry pickers have an NFC (Near Field Communication) chip on their necks, so that every time a picker has filled his/her punnet and takes it to the gathering point, it is automatically registered how many kilos of strawberries were picked, who picked them, and where they picked them. The farmer has programmed in the information about the location of the fields and the actions and can now monitor more efficiently which field has decreased profit and should be “formatted” altogether.

To sum up, computers and programming are present everywhere in everyday life. However, very often the users are not even aware (and hopefully don’t need to be) that they are using a computer and perhaps even programming it.

In these cases, the concept we are dealing with is embedded systems, and/or IoT (Internet of Things) if the device is connected to the web, like in the case of the tractor and farmer.

So, the previous examples concerned giving unambiguous instructions. However, these examples included very different types of communication situations. Interpersonal communication, turning the switches and pressing the buttons of a microwave oven, and timing a digital television adapter are all parallel when it comes to programming, but they include the use of different tools. In programming, the choice of tools depends on the devices or tools available for performing the task at hand. Interpersonal communication can happen via talking, writing, or a combination of the two. Similarly, programming often allows choosing between different methods of implementation, depending on the nature of the task.

Although programming is for the most part performed on a computer, it is always good to have a pen and paper on hand. The most difficult part of programming for a beginner is that the person often can’t bear to sit down with a pen and paper and think about what needs to be done. If for example you are designing a Battleship game, you first need to play the game multiple times in order to get an idea of all the possible things that you will face while designing the game.

Programming has multiple levels depending on which tools are used for completing the task at hand. Advanced high-level tools allow working with concepts and expressions which, at their best, are similar to concepts and expressions in a natural language, whereas low-level tools require working with simple and rudimental concepts and expressions.

This recipe for making a sponge cake can be considered an example of programming:

Sponge cake

6       eggs
1,5 dl  sugar
1,5 dl  flour
1,5 tsp baking powder

1.  Whip the eggs and sugar into a froth.
2.  Mix the flour and baking powder.
3.  Blend the egg-sugar froth and flour mixture together.
4.  Bake for 45 mins in 175°C

The recipe if clearly written for a person, more specifically for someone who knows quite a lot about baking. If the same recipe was written for a person who has never baked anything in his/her life, the recipe above would not be even nearly sufficient - it would have to include many tips related to baking: pre-heating the oven, the art of whipping ingredients to a froth, etc.

Instructions written for a machine differ considerably from the instructions written for a person. A machine doesn’t automatically know how to ask for advice when it runs into a new and unexpected situation. It operates exactly according to the instructions it has been given, whether or not they are sensible in the current situation. Unlike people, a machine loyally repeats the instructions it has been given without succumbing to creativity. Because of this, programming languages today require presenting the instructions to a machine in a very precise format and taking into account all the possible emerging situations. [MÄN]

#

1.2 On programming languages

This section is a brief introduction to the basic principle of computers and programming languages. For more information on the topic, refer to the course materials for Computer Organization and Architecture and Operating Systems. The topic is also discussed briefly in chapter Lukujen esitys tietokoneessa.

1.2.1 Processor and machine code

The most important parts of the computer are the processor and memory. An essential feature of the processor is that it knows which command to run. The command is usually stored in the IP register (Instruction Pointer, also known as PC = Program Counter). The IP register points to a memory slot that holds the command to run. The processor actually works in a simple way:

  1. Get the command from the memory slot indicated by the IP register
  2. Increment the IP register so that it points to the next command
  3. Run the command (can change the IP with JUMP commands)
  4. Continue from 1.

Registers are fast memory slots inside the processor. Commands are usually low-level, e.g.

  • retrieve number from memory slot 7F34 to the AX register
  • Add the value of BX register to the AX register

Each command has a numeric value in the form of bits. For example the command

  • put number 62 (hex) in BL register

in an Intel x86 processor would be in form

B3 62

and in memory in binary form

10110011 01100010

To sum up, the programming task here would be to enter the correct binaries to the memory of the computer. Because binaries are hard to read by humans, they are usually presented as hexadecimals. Even that is not trivial, remembering that B3 means “put in BL register”. This is why an assembly language is usually used: it has almost a 1:1 equivalence with machine language binary and human-readable mnemonic. In one assembly language (there are many variants) the previous command would be

mov bl,$62

First, computers were programmed by entering command numerals directly. Then with the advent of assembly languages, commands were typed in assembly and compiled to numerals in order to get the correct program in the computer’s memory.

Because processor commands are rudimentary, a lot of them are needed to make up even the simplest of programs. Especially when reading user input or a file. This is why an operating system is needed: to offer the most frequently used features out-the-box. Nevertheless, writing even a small program in an assembly language would require a lot of lines of code.

Since the 1950s, programming languages were developed to make writing programs easier and clearer than with assembler. Fpr example, Fortran (1957), Lisp (1958), Cobol (1959), and Pascal (1970) are still used today. By the 1970s, there were already dozens, even hundreds of languages when including all the small languages.

1.2.2 C language and robot

A language compiler is a program that reads a human-readable (e.g. C language) program file (text file) and produces a binary format executable machine language file that can then be run. For this reason e.g. in Windows systems a executable file has the file extension .exe. When the program is run, the operating system puts the program code in the computer’s memory and points the program counter to the first command in the program.

C language (1972) is the most known of all compiled high level languages developed in the 1970s. Its idea (as with its predecessors) is to raise the abstraction to a higher level, so that we can for example state:

int a = 15;
int b = 23;
int c = a + b;

If this was written in machine language, the programmer would have to know which memory slots to use for storing variables a, b, and c. In C programs (and C# programs used on this course) the compiler takes care of used memory slots, e.g. all references to variable a are compiled by the compiler to a reference to the memory slot reserved for a.

The course exercises have as example a small robot that knows only a few commands. The robot works similarly to a processor. For example the previous part of a C program (which is exactly the same in C#) would be:

You can try the robot by clicking the Step button. As an exercise, you can change it to count the sum of all numbers on the Input line (however, this requires that the 0 on the line stops counting). You can put more numbers on the Input line by placing them in Preset input and clicking Reset. Click Run to get the robot to run the entire program.

The yellow line on Program is equivalent to the processor’s IP register which points to the command to run.

The used language is basically the robot’s assembly language.

If commands are given numeric values (which they actually have internally), for example:

00 = INPUT
01 = OUTPUT
02 = ADD
03 = SUB
04 = COPYTO
...
09 = JUMPIFNEG

this program would be compiled to the robot’s “machine language” like this:

00 04 00 00 02 00 01

where some of the commands take up two bytes (a byte is eight bits, presented in two number pairs), for example COPYTO which has the command’s numeric value and the target address for the command (memory slot 00 here).

Then we could have a C compiler that would compile the previously described program to these numbers. Memory slots a and b would be compiled to places on the Input line. The same program could also be made using memory slots:

This is already very similar to a carefully written C program. One of the tasks of the compiler would be to decide that references to variable a mean memory slot 00 and references to b mean memory slot 01.

#

Exercise: Robot's machine language

What would this program be in the robot`s "machine language"? Separate bytes with whitespace.

 

1.2.3 Byte languages

C language was the majority language from late 70s to late 80s. During the 80s, a backward compatible language, extended with objects was made: C++(1982). It was also a compiled language. In the 90s, Java was developed (1995), originally as a language for different embedded systems. Java also mended some known problems with C++. Java had some significant differences compared to C++:

  1. Java is not compiled directly to machine language, but into an intermediate language. The intermediate language file is run with a processor-specific program called Java. The Java program (Java Virtual Machine) reads the intermediate byte code (cf. robot language in numeric format) and executes it step by step. Java was not the first byte-code-based language, but it is currently the most known virtual-machine-based language.
  2. Java has automatic memory allocation, meaning that the programmer doesn’t need to remember to free the memory slots that have been reserved. Automatic memory allocation already existed before Java.
  3. With Java, it is not possible to point to a memory slot that has not been reserved for this purpose (i.e. there are no pointers in Java).

The principle of byte code is that there is no need to make a compiler for each processor architecture and operating system separately. One compiler is enough to generate the intermediate language file (usually .class in Java). On the other hand, executing the program requires interpreting the intermediate language to actual processor machine language, and at first Java programs were slower than C programs. Nowadays Java compilers are more developed, and byte code is also compiled to machine language while it is executed (JIT = Just In Time compiling), meaning that if the same code is executed again, it has already been compiled and executing speed is no different than with C code.

Popularity of Java skyrocketed in the latter half of the 90s. VL’s opinion on why this happened: >“The reason is automatic memory allocation that lowered the number of errors that programmers usually made. Java also had functional strings, which were missing from the C++ standard at the time. The C-like syntax also significantly lowered the threshold to switch languages.

Microsoft had put a lot of effort in C++, but they also noticed the popularity of Java and started using it, adding their own features into it. This cause licensing disputes with Sun, the company that developed Java. Microsoft then started developing its own language which would have all the good qualities of Java. The resulting language was C# (C sharp, 2000). The languages were very similar, making it easy for programmers to switch between them.

1.2.4 C# and Jypeli

Around 2008-2009, the Faculty of Information Technonology in University of Jyväskylä started planning a programming course suitable for young people. It was quite clear that it would contain making games. At the time, Microsoft had great environments (Visual Studio) and libraries (XNA) for making games with C# and get them to work on both computers and phones (Windows Phone). However, programming games with XNA was too challenging, which is why Jypeli library was developed to hide away “extra” details that would hinder a beginner’s enthusiasm. The resulting course, “Nuorten pelikurssi” was a success. At the same time, university students had problems with motivation on Java-based programming courses. Many university students are also interested in games, which is why the theme of the first programming course was switched to game programming, also changing the tools to Jypeli and C#. Making “more meaningful” programs significantly increased the pass rate of Programming 1. Just printing “Hello World” no longer piques interest in the 2010s.

1.2.5 Other languages

A couple of popular languages were mentioned above, C, C++, Java, and C#. They basically share the same roots. Intermediate language interpreting was also mentioned. One very well-known, originally interpreted language is Basic (1964). Its idea was that compiling stage is omitted and the program code is executed directly row by row. Nowadays Python (1991) is a very popular interpreted language. A different approach to programming is functional programming, which can be done with for example Haskell (1990), Scala (2004), and F# (2005).

Javascript is a language used in browsers that makes the originally static HTML pages “come alive”. For example, this material is viewed on an application called TIM, where a server program written in Python and Haskell transmits Javascript (1995) and HTML (1993), which allows the browser to form an interactive text. Additionally, nowadays TIM is written with TypeScript (2012) instead of Javascript, which is then compiled into Javascript. In 3D graphics, shader languages like GLSL and HLSL are used regardless of which languages other parts of the application using the graphics is written in. In addition to these, there are countless different DSLs (domain specific languages). In summary, writing one program may require knowing multiple different programming languages.

The popularity and history of different languages can be read about in the links below. The popularity of languages can be measured in multiple ways, so take different indices with a pinch of salt.

Nevertheless, the example language on this course is C#.

#

2. First C# program

2.1 Coding a program

C# (pronounced c sharp) programs can be coded with any text editor. There are dozens, even hundreds of text editors, so naming just one for coding purposes is hard. However, some have been specifically designed for programming. These kinds of text editors can automatically format the source code (or just code in short) that the programmer produces so that reading is easier and therefore understanding and editing it faster. Programmers favour editors such as Vim, Emacs, Visual Studio Code, Sublime Text and NotePad++, but other text editors are presumably as good as these. Any text editor is applicable to coding the examples at the beginning of this material.

Code, source code = A file produced by the programmer from which the program itself is either compiled or interpreted into machine language that the computer can understand.

Next, write a C# program similar to the example below and save it for example under the name HelloWorld.cs. The established filename extension is .cs, which stems from the name of the used programming language and which we will in this course as well. Be careful when you save a file because some text editors save files with the extension .txt by default, and in this case the file name can accidentally become HelloWorld.cs.txt.

#
To demonstrate, here is a video of coding a program with Notepad++ (In Finnish) Lecture 1 (3m18s)
#
And a corresponding video with the Sublime text editor (in Finnish). Lecture 1 (2m55s)


#
public class HelloWorld
{
    public static void Main()
    {
        System.Console.WriteLine("Hello World!");
    }
}

 

#

Animation: study the meaning of the words and the function of the program

This program should print the text

Hello World!

In order to try out the functionality of this program, it must first be compiled into a form that the computer can understand.

Compiling = transforming the source code into a runnable program.

When you click the Aja(‘Run’) button in TIM, the program is first transformed into machine language format and if the compilation is successful, the program is run and its output is printed on the screen. More information about these phases is presented in the sections to follow. Before that, here are some tasks where you can test your skills, so to say.

Examples of the HelloWorld program coded in different programming languages can be found e.g. here:

#

Task 2.1

Change the code below so that it prints your name on one line and your home town on another line. If you want to print two lines, write the print statement two times.

//
        System.Console.WriteLine("Hello World!");

 

#

Change the program so that it prints your name IN CAPITALS. You can only use the asterisk (*) and space.

//
       System.Console.WriteLine("*******      *       *     *");
       System.Console.WriteLine("   *         *       * * * *");
       System.Console.WriteLine("   *         *       *  *  *");
       System.Console.WriteLine("   *         *       *     *");

 

#

You can also do the same like this:

//
        System.Console.Write("*******      *       *     *\n" +
                             "   *         *       * * * *\n" +
                             "   *         *       *  *  *\n" +
                             "   *         *       *     *\n"  );

 

Try what happends and why if you leave away all letters \n.

#

2.2 Compiling and running programs

In order to compile and run a program, a C# application developer has to be installed on the computer. If you use Windows, a sufficient starting tool is Microsoft .NET SDK (Software Development Kit). With other operating systems, one of the options is Novell Mono. Many of the exercises on this course can be completed with the Mono application developer, but the instructions and examples in the material are for the Windows development environment. Once again, the use of all the features of the Jypeli library is only possible in the Windows environment.

For example, the TIM environment that you are using is implemented (in Python, Haskell, and Javascript) so that the text you write is relayed to a Linux server which saves it in a temporary file and compiles it with the aforementioned Mono compiler. If the compilation is completed without errors, the created assembly language program is run on the Linux server and the output is captured and shown on the browser window. All these phases only take a few seconds.

Next, we will learn to complete these phases manually in order to gain a better understanding of what happens in the background.

More information about and installation of the .NET development tools can be found on the course homepage:

#
Compiling the HelloWorld program in command prompt (in Finnish) Lecture 1 (2m53s)

When the application developer has been installed, start the command prompt (cmd in short) and move into the folder in which the HelloWorld.cs file is saved. The program is compiled with the command:

csc HelloWorld.cs

and in MacOS with the command:

mcs HelloWorld.cs

The command csc is short for C Sharp Compiler. After compiling, a file with the name HelloWorld.exe appears in the folder. This file can be run like any other program by entering the name of the program:

HelloWorld

and in MacOS with the command:

mono HelloWorld.exe

The program should print the text Hello World! on the screen, as in the image below.

Image 1: Compiling and running the HelloWorld program in the Windows command prompt.
Image 1: Compiling and running the HelloWorld program in the Windows command prompt.

Note that while compiling, the file name needs to be written with its .cs extension.

If you get the error message

'csc' is not recognized as an internal or external command, 
operable program or batch file.

then the compiler csc.exe is not found in the so-called search path, aka in the Windows PATH environment variable. Adding a program in the search path can be done with the command:

#
Exercise 2.2

md:Open the attached material in a new window (*ctrl+click* the link) and complete the exercises there. Then answer the questions in the following quiz: >\ Which commands need to be re-entered once the sourse code has been modified?

2.3 The structure of the program

Although the “only significant line” in our first program is

System.Console.WriteLine("Hello World!");

the C# language requires surrounding information about which part of the program contains this statement and from which part the program should start. This adds a little to the number of code lines in this program that in itself is simple. In some languages, it is enough to simply write the print statement. Generally, a low number of lines has no intrinsic value, which is why the (low) number of lines is not enough to define which language is the best.

The HelloWorld.cs program we coded (or actually the text file that we wrote) is almost the simplest possible C# program. Below are the two lines of the simplest possible program.

public class HelloWorld
{

The first line defines the class, the name of which is HelloWorld. At this stage, it is enough to think of the class as a “home” for subroutines (aka subprograms). Subroutines will be discussed later. On the other hand, a class can also be compared to a “cookie cutter” - it is a building instruction for creating objects (or “cookies”). During the run-time of a program, objects can be created with the code inside a class. Objects can also be destroyed. One class can be used to create many objects of the same type, exactly like one cookie cutter can be used to make a lot of (almost) identical cookies.

Each C# program contains at least one class, but there can be multiple classes as well. The name of the class that contains the program code is preferably the same as the name of the file. If the name of the file is HelloWorld.cs, it is recommended that the name of the class is also HelloWorld, just like in the example. At this stage, it is not absolutely necessary to fully understand what a class exactly is - this will become clearer later.

Note: C# is case sensitive. Be careful when you write class names.

Note: The name of a class in the C# language is always capitalised. Do not use Scandinavian letters, such as Ä, Ö, Å.

#

Here the 'System' in the print statement is not capitalised. If you try to run this program, the compilation will fail and an error message will be shown. Change the code to make the program work. Try to switch between cases with the other letters as well.

//

    system.Console.WriteLine("Here the letters are printed exactly as they are.");

 

The word public before the word class is a type of an access modifier. The access modifier allows the class either to be displayed without limitations or with some limitations to others (classes) or to be hidden completely from others. The word public means that the class is public from the point of view of other classes, as most classes usually are. Other access modifiers include protected, internal, and private.

The access modifier can also be blank, which will automatically define the class as internal. Subroutines will be discussed later, but it can be mentioned that accordingly, if the access modifier of a subroutine is left blank, it automatically becomes private. However, in this course we will practice coding public classes (and subroutines), in which case the word public will almost always be written in front of the class and subroutine. Notice however that when we will discuss object variables (attributes), their access modifier will almost without exception be private.

Classes and subroutines are usually defined with the access modifier public. Attributes, on the other hand, are defined as private.

The second line contains a left brace {. In many programming languages, parts that belong together are grouped or gathered within braces. A left brace is an opening brace, which in this case tells the compiler that this is where the contents of the HelloWorld class start. For every opening brace there must be a closing right brace }. The ending brace of the HelloWorld class is in line five, which is also the last line of the program. The area between the braces is called a block.

public static void Main()
{

Line three defines (or more specifically introduces) a new subroutine named Main. Because it uses this name, it is the main program of the class. The words static and void are always a part of the introduction of the Main subroutine. static means that the subroutine is class-specific (as opposed to object-specific, in which case the word static is not used). void means that the subroutine does not return any information. These modifiers will be discussed in detail later. Main may also return a value, in which case the word void is replaced with int, but this feature will not be used during this course.

Similarly to classes, the contents of the main program are also coded within braces. In C# the execution of program code always starts from the main program (Main) of the executed class. Of course, many things happen on the background even before this.

System.Console.WriteLine("Hello World!");

On line four, the text Hello World! is printed on screen. In C# this is accomplished by requesting the .NET environment’s Console class in the System class library to print the text using the WriteLine() method.

Note: in literature, it is common to refer to subroutines by writing parentheses after the name of the subroutine, for example WriteLine(). This writing style emphasises that we are referring to a subroutine, but depending on the context the parentheses can also be omitted (but not in program code). This material usually applies the latter method of omitting the parentheses, depending on the situation.

Libraries, objects, and methods will be discussed more in section 4.1 and in chapter 8.

The character string to be printed is written in quotes inside parentheses (Shift + 2). This line is also the only statement in the program. Statements can be thought of as being individual actions that form the program. In C#, each statement ends with a semicolon. Because a semicolon marks the end of a statement, white spaces, such as line breaks or spaces, make no difference to the function of a program in the syntax of C#. They are highgly relevant to the readability of the code, however. Note that forgetting to add a semicolon is one of the most common errors in programming, or more specifically one of the most common syntax errors.

Syntax = the set of grammatical rules in a certain (programming) language (such as C#).

#

Task 2.3

The following is a blueprint of a program that needs to be made. However, the compiler will not recognise the words, so replace the words with C# language words. In other words, code a program that prints your name. Notice that the program will not be compiled if there is even one unidentified word in it.

//
public-access-modifier class ClassName{

  public-access-modifier class-specific returns-nothing Mainprogram(){

     Print("Name");
  }

}

 

Note that in the example below the value of variable a is printed by forming a new string which combines another string and value of a with a plus operator. In this way, we can give the WriteLine subroutine just one string as parameter as is supposed to. The WriteLine subroutine cannot be given a list with a comma separator like in some languages.

#

Task 2.4

Try which parts in the code can contain extra spaces or even line breaks without changing the functionality of the program

//
public class Empty
{
    public static void Main()
    {
       int a = 3;
       System.Console.WriteLine("the value of a is " + a);
       a++; // Increments a by 1
       System.Console.WriteLine("and now it has increased by one: " + a);
    }
}

 

#
Check your understanding

Where can you add extra spaces or line breaks in C\# without changing the way the program works?

#
Check your understanding

Which of the following statements concerning the program in task 2.4 are true?

2.3.1 Error types

Programming errors can be broadly categorised into syntax errors and logical errors.

In the previous example, it was studied where extra spaces or line breaks are allowed. When the program could not be compiled, it was due to a syntax error. When the program worked, but the output looked different, it was due to an error in writing style (or a difference in opinion).

A syntax error prevents the compilation of the program even if the meaning (aka the semantics) of the program was correct. Examples of syntax errors include spelling mistakes or forgetting to add a semicolon at the end of a statement.

Logical errors happen when the semantics, aka the meaning, of the program is faulty. They are harder to notice because the program is compiled successfully despite semantic errors. The program may even seem to be working exactly as intended. If a logical error goes unnoticed even during testing, the consequences can be catastrophic, depending on the purpose of the software. Here is one commonly known example of a logical error which was luckily noticed in time and correcting it prevented a wide-spread disaster:

#

An example of a run-time error. The program prints the division of 10 by 2. Try to run the program. If the divisor is 0 instead of 2, a run-time error occurs, because division by 0 is impossible. Try it.

//
        int divisor = 2;
        System.Console.WriteLine("10/" + divisor + "=" +10/divisor );

 

2.3.2 Interpreting compiler error messages

The following is an example of a syntax error in the HelloWorld program.

#
public class HelloWorld
{
    public static void Main()
    {
        System.Console.Writeline("Hello World!");
    }
}

 

The program contains a minor spelling error, which (without aid) is fairly hard to notice. Study the error message given by the csc compiler.

HelloWorld.cs(5,17): error CS0117: 'System.Console' does not
contain a definition for 'Writeline'

The compiler tells us that row 5 and column 17 in the file HelloWorld.cs contains the following error: ‘System.Console’ does not contain a definition for ‘Writeline’. This is true, because the word Line in WriteLine should be capitalised. After fixing this spelling error, the program works.

Unfortunately, the content of the error message does not always describe the problem very well. In the following example, a semicolon has been misplaced. Try to find it yourself first before continuing or running the program.

#
public class HelloWorld
{
    public static void Main();
    {
        System.Console.WriteLine("Hello World!");
    }
}

 

The error message, or error messages look something like this, depending on the compiler:

HelloWorld.cs(4,3): error CS1519: Invalid token '{' in class,
struct, or interface member declaration
HelloWorld.cs(5,26): error CS1519: Invalid token '(' in class,       
struct, or interface member declaration
HelloWorld.cs(7,1): error CS1022: Type or namespace definition,
or end-of-file expected

The first error message points to line 4, when in reality the stem of the problem is in line 3. In other words, the compiler error messages are not helpful in this case; on the contrary, they instruct us to do something we don’t want to, and should not, do.

#

Task 2.5

Try one of the following and other possible error types in the program below. Remember that by clicking Alusta you can return to the starting point

//
public class Errors
{
    public static void Main()
    {
       int a = 5;  // Try to replace this with a capital A
       System.Console.WriteLine("value of a is " + a);
    }
}

 

More examples of interpreting error messages can be found on the extra material for the course (page currently available in Finnish only).

2.3.3 White spaces

As we tried out in a task earlier, the HelloWorld example program could be coded, without altering its function, in the following alternative format.

#
   public class HelloWorld
                             {


        public static void Main()
     {
System.Console.WriteLine("Hello World!");
       }


   }

 

Furthermore, the program could also be coded as follows.

#

Task 2.6

Fix the line wrapping and indentation.

//
public class HelloWorld { public static void Main() {
System.Console.WriteLine("Hello World!"); } }

 

Or even by writing all of the code on one line, try it.

Although both of the examples above are syntactically correct, in other words, they abide by the grammatical rules of C#, their readability is significantly lower than in the original program. C# has commonly agreed coding conventions that define how program code should be written. When everyone writes code in the same way, it is easier to read the code. The examples in this material are coded according to these conventions. Links to coding conventions can be found on the course extra information page (currently only available in Finnish):

A string of text is written between " quotes. While processing strings, spaces, tabs, and line breaks are significant. Compare the output of the programs below.

#
        System.Console.WriteLine("Hello World!");

 

The line above prints out:

Hello World!

whereas the line below prints out:

H e l l o    W o r l d !
#
        System.Console.WriteLine("H e l l o    W o r l d !");

 

To make reading easier, white spaces are used in front of lines to indent blocks. It is customary that after each opening brace the code lines are indented by four spaces, and the indentation is decreased accordingly by four spaces after a closing brace. In C#, a pair of braces is usually located in the same column. Usually IDEs automatically format the text, which is definitely a feature worth using if you don’t know how to format the text properly yourself.

2.4 Commenting

“Good programmers use their brains, but good guidelines save us having to think out every case.” -Francis Glassborow

The C# language contains three different types of comments and four different types of annotation:

annotation meaning
// single-line comment
/// dokumentation comment
/* beginning of a multiline comment
*/ end of a multiline comment

Commenting and documenting also includes abiding by coding conventions, for example using correct indentation and giving descriptive names to variables etc. Program code should be readable to a person familiar with some other language.

Source code is usually hard to understand just from reading the code itself. This is why you can and you should add descriptions or comments in your code. Comments help both the programmer himself/herself as well as any person who reads or maintains the code in the future. Many things may seem obvious at the time of writing, but even after a week you may wonder what purpose each line of your code serves.

The compiler ignores comments, so they won’t affect the functionality of the program.

// Single-line comment

Single-line comments start with two slashes (//). They’re effective until the end of the row.

/* This comment
   lasts for
   multiple
   lines */

A comment that starts with a slash and an asterisk (/*) lasts until another asterisk and slash ends it (*/). Note that there is no space between the asterisk and slash.

#
Instructions for readable code (in Finnish) Lecture 1 (8m3s)
#

Task 2.7

Try out different types of comments in different places in the program code below.

//
public class HelloWorld
{
    public static void Main()
    {
        System.Console.WriteLine("Hello World!");
    }
}

 

For example, the comment line /* cat */ can be added in the same places where spaces could be added in a previous task. Respectively, you cannot write a comment line in places where you cannot add spaces either.

2.4.1 Documentation

The third comment type is the documentation comment. Documentation comments have a specific syntax, and abiding by its rules allows transforming documentation comments into a summary, which can be viewed in a browser or printed neatly on paper, for example.

A documentation comment should be written in front of each class, main program, subroutine, and method (subroutines and methods will be discussed later). In addition, each C# file should start with a documentation comment that clarifies the meaning, author, and version of the file.

Documentation comments always start with three slashes (Shift + 7). Accordingly, each following documentation comment line starts with three slashes as well.

/// This
/// is
/// a documentation comment

Documentation utilises tags. If you have ever written HTML pages, this type of notation should be familiar to you. Documentation comments begin with a start tag, like <example>, which is followed by the content of the comment. Comments end with an end tag, like </example>, i.e. otherwise just like the start tag but with one slash after the first angle bracket.

C# tags include for example <summary>, which is a short summary of the block of code to follow (for example the main program or a method). The summary ends with the end tag </summary>.

During compilation, documentation tags can be saved in a separate XML file, from which they can be easily transformed into browsable HTML pages. You can come up with more tags, but the list of recommended tags is sufficient for the purposes of this course. Information of the recommended tags can be found in the C# documentation:

We can now add the following C# comments to the beginning of the HelloWorld program:

#
//
/// @author  Antti-Jussi Lakanen
/// @version 28.8.2012
///
/// <summary>
/// A program example that prints the text "Hello World!"
/// </summary>

public class HelloWorld
{
  /// <summary>
  /// Main program that takes care of printing
  /// </summary>
  public static void Main()
  { // This is where execution of the program starts, the so-called "entry point" of the program
    // the following statement prints the text
    System.Console.WriteLine("Hello World!");
  } // execution of the program ends here
}

 

The author is given at the beginning of the program. This is followed by the first documentation comment (note the three slashes), which is a brief description of the class. Nore that in some documentation summaries only this first sentence is shown. Click the link Document and study the generated documentation by clicking the links in it. The content of the documentation is gathered from the program’s documentation comments that start with ///.

Documentation comments ensure that a program could eventually be documented to the similar extend as Jypeli.

Note that the documentation indicator /// is not used anywhere else (like in front of a subroutine or a class) in the program but in front of documentation comments. Regular single-line comment indicators // or multi-line indicators `/* … */ are used within the code.

Documentation is an extremely important part of programming. As the number of classes and lines of code grows, documentation eases both your own work as well as the work of future users and administrators. The importance of documentation is emphasised by the fact that as much as 40-60% of the work time of administrators is spent on trying to understand how the program under revision works. [KOSK][KOS]

#

Task 2.8

Add documentation comments in front of the class and the main program. Then click the link Document and study the generated documentation

//
public class Empty
{
    public static void Main()
    {
       int a = 3;
       System.Console.WriteLine("the value of a is " + a);
       a++; // a is incremented by one
       System.Console.WriteLine("and now it is incremented by one: " + a);
    }
}

 

#
Check your understanding

Which of the following concepts are you familiar with? Revise if necessary

#

3. Algorithms

“First, solve the problem. Then, write the code.” - John Johnson

3.1 What is an algorithm?

When we write machine-readable instructions, the action to be performed needs to be written as a series of simple actions. This series of actions needs to be unambiguous, which means that in each situation it can offer only one course of action, and it cannot contain any contradictions. An unambiguous description of the series of actions that need to be taken in order to perform a task are called an algorithm.

The coding process of a program can start with outlining the necessary algorithms, i.e. by listing the necessary actions to perform a task:

Brewing coffee:

1.  Fill the coffee pot with water.
2.  Boil the water.
3.  Add the coffee grounds.
4.  Let the coffee even out.
5.  Serve the coffee.

In general terms, an algorithm is a series of actions defined as specifically as possible, consisting of unambiguous step-by-step actions that are necessary to solve the task at hand.

3.2 Specification

When inspecting almost any assignment, you will notice that the performance of the task consists of clearly distinguishable parts of the task. The way in which a single part of the task is performed has no effect on performing the other parts. Only the fact that each part is performed has relevance to the result. For example, each part of the coffee brewing task can be divided into smaller parts:

Brewing coffee:

1. Fill the coffee pot with water:
  1.1.  Place the pot below the tap.
  1.2.  Open the tap.
  1.3.  Let the water run until there is enought water in the pot.
  1.4   Close the tap.
2.  Boil the water:
  2.1.  Place the pot on the stove.
  2.2.  Turn the hotplate on.
  2.3.  Let the hotplate warm up until the water boils.
  2.4   Turn the hotplate off.
3.  Add the coffee grounds:
  3.1.  Measure out the coffee grounds.
  3.2.  Mix the coffee grounds into the boiling water.
4.  Let the coffee even out:
  4.1. Wait until most of the coffee has blended into the water.
5.  Serve the coffee:
  5.1. This is a story on its own...

The solution to the coffee brewing problem was divided into five phases. The algorithm of the solution contains five statements to perform. Upon closer inspection, it turns out that each of these five statements is divisible to even smaller phases, i.e. the main algorithm of the solution can be divided into subalgorithms which present steps to solve each phase.

Writing algorithms turns out to be a hierarchical process, where a task is divided into parts which are specified until each part of the task is so simple that there is nothing ambiguous about it.

3.3 Generalisation

One important stage in writing algorithms is generalisation. Generalisation means trying to locate all the factors in an algorithm that depend on the task at hand and analysing if these factors could be replaced with more general factors or even removed completely.

3.4 Exercise

#

Task 3.1 Brewing tea

Study the algorithm for brewing coffee above and write a corresponding algorithm for brewing tea. Compare the two algorithms: what similarities and differences are there? Is it possible to come up with an unambiguous algorithm that would cover both brewing coffee and brewing tea? Is it possible to come up with an algorithm that would cover hot chocolate and hot toddy in addition to the two?

 

3.5 Sequence

Similar to the recipe in chapter 1 and other instructions written for people, also instructions for computers are read top-down, unless defined otherwise. For example, instructions for drawing a snowman could be presented in the simplified manner below.

Draw a circle of radius 20 cm to the point (20,80)
Draw a circle of radius 15 cm on top of the previous circle 
Draw a circle of radius 10 cm on top of the previous circle

The code above is not written in any programming language yet, but it already contains the idea of how to draw a snowman on a computer. We will draw a snowman in C# in the next chapter.

#

Here is an attempt at adding a ball before it has been created. It is not possible, which is why the program cannot be compiled

//
        Add(ball);
        Level.Background.Color = Color.Black;
        PhysicsObject ball = new PhysicsObject(200,200,Shape.Circle);
        ball.Color = Color.Yellow;
        // Move the adding of the ball (i.e. the first line Add(ball);) here

 

#

Here, the background colour and the colour of the object is defined multiple times. The last change stays in force.

//

        Level.Background.Color = Color.Black;
        Level.Background.Color = Color.Blue;
        PhysicsObject ball = new PhysicsObject(200,200,Shape.Circle);
        ball.Color = Color.Yellow;
        ball.Color = Color.Black;
        Add(ball);

 

An example of an algorithm: let’s assume that you face a situation where you have an array of numbers and each number in this array should have the same value as the first number in the array. In the following task, you can write an “algorithm” for this situation by using the Tauno program.

Drag the elements in the array in Tauno so that you finally have the required result. At the same time, look at what kind of code Tauno generates for you. This is an algorithm for performing this task in C#. If you want to start again, hide Tauno and show Tauno again (click Hide Tauno and Click here to show Tauno).

#
You can also watch this video about the use of Tauno (in Finnish) Lecture 1 (3m10s)
#

Task 3.2. Give the same value to all elements

Using Tauno, make a program in which all elements in the array have the same value as the first element from left.

 

Try to think if the order of the statements in your solution to the Tauno task could be altered. If it could, your code is parallel; if it couldn’t, your code is purely sequential.

#

Using Tauno, make a program in which the first three elements in the array have the same value as the first element and the last three elements in the array have the same value as the last element in the array.

 

#

4. A simple graphical C# program

The following example utilise the Jypeli programming library developed in University of Jyväskylä. Originally the library was designed and implemented for the Youth game programming course, but it was found suitable for the level of the Programming 1 course as well. You can download the library from here:

Although we won’t be needing the Visual Studio IDE at this point yet, it is wise to install in now, because you need to have Visual Studio installed before XNA and Jypeli.

4.1 What is a library?

C# programs consist of classes. Classes contain methods (and subroutines/ functions), which perform tasks and possibly return values after performing these tasks. A method could, for example, calculate the sum of two numbers and return the result, or draw a circle of the requested size. Methods related to the same topics are collected into classes and classes are further collected into libraries. The idea behind libraries is that there is no need to redo something that has been done by someone else already. In other words, there is no need to reinvent the wheel.

The most significant library for a C# programmer is the .NET Framework Class Library (FCL). The documentation of the class library is worth exploring, because it contains multiple methods that are very useful. The documentation can be found on Microsoft’s site at

Class documentation contains information about all the classes and their methods (and subroutines) in a library. Usually available in at least WWW format.

#

Task 4.1 methods in the Console class

Find the Console class of the System namespace. What other methods does the Console class contain apart from WriteLine()?

 

4.2 The Jypeli library

Development of the Jypeli library started in University of Jyväskylä in spring 2009. The examples in this material use version 4. The Jypeli library contains classes and methods which make it easier to include for example physical and mathematical phenomena as well as characters and their movements into your own programs.

4.3 Example: Snowman

#
Lecture video shows how to create a simple object: Video (8m34s)
#
Draw a snowman using the Jypeli library. Watch the video demonstration. Lecture 2 (6m43s)
#
//
// Utilise the JYU Jypeli library
using Jypeli;

/// @author  Vesa Lappalainen, Antti-Jussi Lakanen
/// @version 22.12.2011
///
///
/// <summary>
/// A class where we practice drawing cirles on screen.
/// </summary>
public class Snowman : PhysicsGame
{

  /// <summary>
  /// The main program starts the "game" as is customary in Jypeli
  /// </summary>
  public static void Main()
  {
    using (Snowman game = new Snowman())
    {
      game.Run();
    }
  }

  /// <summary>
  /// Draw the objects and zoom the camera so that the entire level is visible.
  /// </summary>
  public override void Begin()
  {
    Camera.ZoomToLevel();
    Level.Background.Color = Color.Black;

    PhysicsObject p1 = new PhysicsObject(2*100.0, 2*100.0, Shape.Circle);
    p1.Y = Level.Bottom + 200.0;
    Add(p1);

    PhysicsObject p2 = new PhysicsObject(2 * 50.0, 2 * 50.0, Shape.Circle);
    p2.Y = p1.Y + 100 + 50;
    Add(p2);

    PhysicsObject p3 = new PhysicsObject(2 * 30.0, 2 * 30.0, Shape.Circle);
    p3.Y = p2.Y + 50 + 30;
    Add(p3);
  }
}

 

The description that follows refers to the line numbers in this program.
Click the link Highlight to show the line numbers.

When you run the program, it should draw a simple snowman in the middle of the screen, as in the image below.

#

Image 2: Snowman drawn using the Jypeli library

For continuation, we will shorten the program and write the repeated main program in a separate file. This way, we can focus on the problem itself. Try to add a fourth circle to the snowman.

#

Task 4.2. Fourth circle

Add a fourth circle to the snowman

//
        Camera.ZoomToLevel(); // tai Camera.ZoomToAllObjects();
        Level.Background.Color = Color.Black;

        PhysicsObject p1 = new PhysicsObject( 2*100, 2*100, Shape.Circle);
        p1.X = 0; p1.Y = Level.Bottom + 200;
        Add(p1);

        PhysicsObject p2 = new PhysicsObject( 2*50, 2*50, Shape.Circle );
        p2.X = 0; p2.Y = p1.Y + 100 + 50;
        Add(p2);

        PhysicsObject p3 = new PhysicsObject( 2*30, 2*30, Shape.Circle );
        p3.X = 0; p3.Y = p2.Y + 50 + 30;
        Add(p3);

 

4.3.1 Running a program

Execution of a program always starts from the opening brace of the main program and moves line by line, top-down all the way to the closing brace of the main program. The main program (just like any other subroutine) can also contain subroutine calls, which move the execution from the main program to the subroutine and then back to the main program (the subroutine that made the call). Subroutines will be discussed in detail in chapter 6. In fact, even the examples from before made subroutine calls, such as Add(p1)

Let’s inspect the most significant parts of the Snowman program.

02 using Jypeli;

First we have to let the compiler know that we want to utilise the entire Jypeli library. Now all the classes in the Jypeli library (and their methods) are in our use. In fact, we don’t even have to use the statement using. But if we leave it out, the compiler will not recognise words such as PhysicsGame. This problem could be solved by stating that it can be found in the Jypeli library:

11 public class Lumiukko : Jypeli.PhysicsGame

And similarly, Jypeli. would have to be added in front of each introduction of a Jypeli tool. For this reason, we can save trouble by simply stating using Jypeli. In fact, if we would have stated this at the beginning of the HelloWorld.cs file:

using System;

for printing, it would have been enough to write:

    Console.WriteLine("Hello World!");

Now, let’s return to inspecting the Snowman program:

08 /// <summary>
09 /// A class where we practice drawing cirles on screen.
10 /// </summary>
11 public class Snowman : PhysicsGame
12 {

Lines 8-10 are documentation comments. Line 11 creates a new class, Snowman, which differs from the style of creating a class HelloWorld example. Here, we are using the Jypeli library for the first time to state that the Snowman class that we are creating is “based” on the PhysicsGame class in the Jypeli library. To be more specific, the Snowman class is inherited from the PhysicsGame class. This way the Snowman class can utilise all of the features in the PhysicsGame class and can add new features to it. Here, we are adding the function of the Begin method, which defines what will be drawn at the beginning of the “game”. In a way, Begin is the “main program” of a Jypeli program.

Drawing and (later) moving objects on screen and utilising laws of physics is much easier by utilising the PhysicsGame class.

14   /// <summary>
15   /// The main program starts the "game" as is customary in Jypeli  
16   /// </summary>
17   public static void Main()
18   {
19     using (Snowman game = new Snowman())
20     {
21       game.Run();
22     }
23   }

Also the Main method, aka the main program, is practically always written in this format in Jypeli, so we won’t have to change it much in the future. We will skip explaining the contents of the main program at this point and simply state that the main program creates a new object (i.e. a new “game”) using the Snowman class which is then run with the statement game.Run(). Due to the structure of the Jypeli library, all of the actual game-related code is coded within their own subroutines. Next, the code that is executed at the beginning of a “game” is coded into the Begin subroutine, which will be executed next.

To be specific, Begin starts from line 29. The first statement is written on line 30.

30     Camera.ZoomToLevel();
31     Level.Background.Color = Color.Black;

The first of these two lines calls the ZoomToLevel subroutine within the Camera object, which makes sure that the “camera” is focused and zoomed in on the right spot. The subroutine doesn’t require any parameters, so the area between the parentheses is blank. The second line changes the colour of the background.

Let it be known that the Camera and Level objects are objects of the game (the variable game in the main program) created from the Snowman class. In reality, the code should be as follows:

30     this.Camera.ZoomToLevel();
31     this.Level.Background.Color = Color.Black;

but when we refer to the object’s own features, the self-reference this. can be left out. Some programmers like to write out the self-reference for clarity, although it is not necessary. This is a typical example of a matter of opinion in programming.

33     PhysicsObject p1 = new PhysicsObject(2*100, 2*100, Shape.Circle);
34     p1.Y = Level.Bottom + 200;
35     Add(p1);

With these three lines, we create a new physics object, a circle, give its radius, y-coordinate, and add it to the “stage”, i.e. the visible area of the program. If the x-coordinate is not provided, it is 0 by default.

More specifically, we create a new PhysicsObject object, i.e. an instance of the PhysicsObject class, which we name p1. PhysicsObject objects are objects that move on the game field and abide by the laws of physics. Within the parentheses, we state what kind of object we want to create - in this case, the width and height (on the Jypeli scale, not in pixels) and the shape of the object. In summary, we are creating a circle (Circle) of radius 100 (width = 2 * 100 and height = 2 * 100). Other shapes in the Shape collection include a triangle (Triangle), an ellipse (Ellipse), a rectangle (Rectangle), a heart (Heart), etc. Objects will be discussed in more detail in chapter 8.

#

Task 4.3 shape of the object

Try changing the shape of the object:

//
        Level.Background.Color = Color.Black;
        PhysicsObject ball = new PhysicsObject(200, 200, Shape.Circle);
        ball.Color = Color.Yellow;
        Add(ball);

 

#

Try changing the size of the object:

//
        Level.Background.Color = Color.Black;
        PhysicsObject ball = new PhysicsObject(200, 200, Shape.Circle);
        ball.Color = Color.Yellow;
        Add(ball);

 

The next line defines the position of the object with its Y-coordinate value:

34     p1.Y = Level.Bottom + 200;

Notice that Y is capitalised. This is an attribute of the p1 object. The x-coordinate need not be defined separately because it is 0 by default and that suits us. To draw circles in specific positions, we need to calculate the coordinates. By default, the centre of the window is the origin of the coordinates, i.e. the point (0, 0). The values of the x-coordinate grow when moving right and the values of the y-coordinates grow when moving up, similarly to “ordinary” coordinates that we learn in school.

#

Try changing the x- and y-coordinates of the object. How can you move the object to the upper right corner?

//
        Level.Background.Color = Color.Black;
        PhysicsObject ball = new PhysicsObject(200, 200, Shape.Circle);
        ball.Color = Color.Yellow;
        ball.Y = 0;
        ball.X = 0;
        Add(ball);

 

The coordinates can also be entered in vector format by providing both of the coordinate components at the same time. For example, in the previous task the ball could have been placed in the position x=20,y=50 by coding:

#
//
        ball.Position = new Vector(20,50);

 

The game object always needs to be added to the stage before it becomes visible. This can be done with the Add method, which takes in the name of the object to be added as its parameter (here p1).

35    Add(p1);

More specifically, we should state that we are adding the object to this game, like this:

35    this.Add(p1);

but as discussed before, self-references can be left out.

#

This example is missing the method call to Add, which is why nothing is added to the stage. Write the method call to the end of the code and run the program again. Also try adding the self-reference 'this.'.

//
        Level.Background.Color = Color.Black;
        PhysicsObject ball = new PhysicsObject(200, 200, Shape.Circle);
        ball.Color = Color.Yellow;

 

The information provided to methods is parameters. The method ZoomToLevel doesn’t take in any parameters, but the Add method takes in one parameter: a PhysicsObject object that we want to add to the stage. Another parameter that the Add method can take in is the layer number to which the object is added. By using the layers you can manage which objects are added on top. The layer parameter can be left out of the method call though, which makes the program decide the best order of the layers by itself.

#

There are two objects here, but one has covered the other. The objects have the same layer number (0) which is why the square covers the circle object. Change the layer number of the circle object(ball) to 1 and rerun the program.

//
        Level.Background.Color = Color.Black;

        PhysicsObject square = new PhysicsObject(200, 200, Shape.Rectangle);
        Add(square, 0);

        PhysicsObject ball = new PhysicsObject(200, 200, Shape.Circle);
        ball.Color = Color.Red;
        Add(ball, 0);

 

Parameters are written in parentheses after the method name and separated by commas.

MethodName(parameter1, parameter2,..., parameterN);

In the following lines we create two more circles in a similar way, but changing the radius and coordinates of the circles.

In the Snowman example, the arithmetic operations of C# are utilised in calculating the coordinates. Of course, we could calculate the coordinates ourselves, but why should we if the computer can do it for us? The basic arithmetic operations of C# are sum (+), subtraction (-), multiplication (*), division (\), and the remainder (%). Arithmetic operations will be discussed in more detail in section 7.7.1.

The middle circle is placed on top of the top circle so that the circles touch. In other words, the centre of the middle circle is located so that its x-coordinate is 0 and its y-coordinate is the position of the bottom circle + the radius of the bottom circle + the radius of the middle circle. If we want the radius of the middle circle to be 50, we need to place its centre in the position (0, p1.Y + 100 + 50), which can be drawn with the statement:

PhysicsObject p2 = new PhysicsObject(2 * 50, 2 * 50, Shape.Circle);
p2.Y = p1.Y + 100 + 50;
Add(p2);

Notice that in addition to setting the Y-attribute of the physics object (set) we can also read or request (get) the value of the parameter in question. In the example above, we do this by simply writing p1.Y to the right side of the assignment operator =.

The following image demonstrates the positioning of the first and second ball.

#

Image 3: The first two balls of the snowman positioned in place

The top circle touches the middle circle. As a practice assignment, calculate the coordinates of the top circle when its radius is 30.

All information about classes, class methods and what parameters each method takes in can be found in the documentation of the library you are using. The class documentation of Jypeli can be found here:

Documentation of Jypeli is in Finnish, but the names of classes and attributes are in English.

#

Task 4.4. Position of objects with a vector

Try changing the position of the object with the call
ball.Position=new Vector(somethingx,somethingy)

//
        Level.Background.Color = Color.Black;
        PhysicsObject ball = new PhysicsObject(200, 200, Shape.Circle);
        ball.Color = Color.Yellow;
        Add(ball);

 

#

This program draws dice. Try changing the values in the faces of the dice.

//
       Level.Background.Color = Color.Black;

       double size = 200;
       PhysicsObject square = new PhysicsObject(size, size, Shape.Rectangle);
       Add(square);

       PhysicsObject face1 = new PhysicsObject(size/4, size/4, Shape.Circle);
       face1.Color = Color.Black;
       face1.X = square.X - size/4;
       Add(face1,1);

       PhysicsObject face2 = new PhysicsObject(size/4, size/4, Shape.Circle);
       face2.Color = Color.Black;
       face2.X = square.X + size/4;
       Add(face2,1);

 

#

4.4 Exercise

Find the class RandomGen in the documention of the Jypeli library. What information can you find about the
NextInt(int min, int max) method?
What other methods does the class contain?

#

 

#

Task 4.5. Randomising the position

Study how the ball changes with each run. Try changing the colour of the ball into a randomised colour.

//
        Level.Background.Color = Color.Black;
        PhysicsObject ball = new PhysicsObject(200, 200, Shape.Circle);
        ball.X = RandomGen.NextInt(-200, 200);
        ball.Y = RandomGen.NextInt(-200, 200);
        Add(ball);
        System.Console.WriteLine(ball.X +" " + ball.Y);

 

The next example teaches the use of the random number generator in the C# library. The example is run in the Mono environment and it gives different results for the random number from the .NET environment. The example demonstrates that the random number in the Mono environment does not work well. Both Jypeli and the Windows .NET environment have this problem with the random number fixed.

#

Task 4.6. Distribution

The code below inspects the distribution of random numbers in the interval of [0,MAX[. Try it. What would happen if you made a coin toss game?

//
       int MAX = 6;
       System.Random rnd = new System.Random();
       int[] t = new int[MAX];
       for (int i=0;i<1000; i++)
       {
           int n = rnd.Next(0,MAX);
           t[n]++;
       }
       System.Console.WriteLine(string.Join(" ",t));

 

#

After running the program above, the elements in array t are printed. Each of the values represents how many times this random number was generated. What is the ratio of these numbers?

 

4.5 Compilation and referring to class libraries

In order to compile the Snowman example program with a C# compiler, you need to save the Jypeli library on your computer. Jypeli utilises not only the XNA library but also free open-source physics and mathematics libraries. In other words, the Jypeli library has built-in physics and mathematics libraries.

Before compiling in the command prompt, copy the following files from the course website (see: Snowman in command prompt(in Finnish)) and paste them in the same folder that contains the Snowman.cs file.

We still need to tell the compiler to use the Jypeli library in order to compile the Snowman code. In addition, the compiler needs the information that this program is made for the use of 32-bit systems (x86). This can be done with the help of the /reference parameter of the csc program (the compiler). The reference to the XNA library that Jypeli utilises is needed as well. Write the following command (all rows on the same line, one space in front of the /) in the command prompt

csc Snowman.cs /reference:Jypeli.dll;
 "%XNAGSv4%\References\Windows\x86\Microsoft.Xna.Framework.Game.dll" 
 /platform:x86

If your operating system does not recognise the command csc, revise the instructions about adding a command to the PATH environment variable in chapter 2.

Tip! The compilation command above is rather long. To make things easier, you can write a file csk.bat (for example in folder c:\bat\) which contains the following two lines of text (paste together the two lines beginning with @ without adding any extra spaces):

@"%WINDIR%\Microsoft.NET\Framework\v4.0.30319\csc" %* /nologo /reference:Jypeli.dll;
Jypeli.Physics2d.dll;
MonoGame.Framework.dll
@if NOT ERRORLEVEL 1 %~n1

So:

@"%WINDIR%\Microsoft.NET\Framework\v4.0.30319\csc" %* /nologo /reference:Jypeli.dll;"%XNAGSv4%References\Windows\x86\Microsoft.Xna.Framework.Game.dll";"%XNAGSv4%References\Windows\x86\Microsoft.Xna.Framework.dll" /platform:x86 /define:WINDOWS
@if NOT ERRORLEVEL 1 %~n1

This will set the reference and platform parameters for you. Make sure that the csk.bat file you created is in the path or at least in the same folder as the file Snowman.cs. After this operation, compiling the program is as simple as:

csk ProgramName.cs

#
Same operation demonstrated on lecture (in Finnish): Lecture 2 (7m21s)

More information about the topic on the course’s extra material page (in Finnish).

5. From source code to the processor

5.1 Compiling

Now, we will inspect more carefully how the C# source code eventually transforms into a format that the processor can understand. When the programmer creates the program’s source code that utilisises the .NET Framework environment, the internal compilation process is divided into two phases. First, the program is compiled into an intermediate language, MSIL (Microsoft Intermediate Language), which is not yet executable in any operating system. During runtime, the finished program is compiled from this indermediary phase code to the desired operating system and processor. The operating system can be for example Windows, macOS, iOS, Android, or Linux. The processor can be for example one of the Intel processors abiding the x86 architecture or with mobile devides for example ARM. This run-time compilation is performed with the so-called JIT compiler (Just-In-Time). The JIT compiler transforms the intermediary code into code that is compatible with the operating system specifically during run-time - hence the name “just-in-time”.

Before the first compilation the compiler checks that the code has the correct syntax. [VES][KOS]

Compilation was performed with the Windows command prompt by using the command

csc Filename.cs

or by utilising the command string in the last section

csk Filename.cs

5.2 Executing/running the program

So, C# produces an executable (or “runnable”) file from the source code. This file is OS-dependent and only executable on the platform on which the compilation was performed. In other words, programs that have been compiled in the Windows environment are not executable in macOS, or vice versa.

Unlike C#, some other programming languages produce OS-independent code. For example, in Java, the file that the compiler produces is in so-called bytecode, which is OS-independent. In order to execute Java bytecode, a Java Virtual Machine is required. A Java Virtual Machine is a program that imitates a real computer which interprets bytecode and executes it on the processor of the target computer. This differs significantly from traditional compiled languages (such as C and C++) in which the compilation needs to performed separately for each different device platform.

#

6. Subroutines

“Copy and paste is a design error.” - David Parnas

In addition to the main program, a program can contain other programs as well. A subroutine is called from the main program, a method, or another subroutine in order to perform a certain task. Subroutines can receive parameters and return a value, similarly to methods. A subroutine can call another subroutine and sometimes even itself (this is called recursion). A real program consists of several subroutines, each of which performs a small task on their own. In this way, a large task can be didived into a set of smaller, easily manageable subtasks.

Subroutines are used because

  • they allow dividing the program into smaller parts
  • they help with structuring the program
  • they are helpful for reusability
  • smaller parts make testing easier

The objects in modern object-oriented languages are actually a collection of object-specific variables (attributes) and subroutines that handle them (methods). In addition, the API (Application Programming Interface) of modern languages is significantly larger than the langauge itself. In addition to subroutine libraries included in a language, also application-specific libraries, which can be very extensive, are used often. The Jypeli library used on this course is one example of an application-specific library. Using existing libraries eases the workload of programmers, because not all has to be done by themselves.

On the other hand, subroutines are also coded by the programmer himself/herself. In practice, it is often the case that the program contains a part that is often repeated almost identically. In these cases, the programmer tries to find the common factors between these parts and tranform them into a subroutine. If the functionality wasn’t completely identical in repeated parts, the difference is relayed to the subroutine as parameters. This way, the same subroutine can do slightly different things with each different call. An example of this will be presented shortly.

However, sometimes subroutines are also coded when there is a problem such as “I need to find the largest number in this array”. In most cases, it makes no sense to write out the search and instead the programmer makes the request: “we should have a subroutine that does this”. The same in writing:

large = Largest(array);

Later on the Largest subroutine (or function in this case because it returns a value) is implemented. Now if the same task would have to be completed again, the same subroutine call could be made again (reuse).

The same subroutine is often called from a program multiple times, but for the sake of clarity it can be reasonable to code independent entities into subroutines as well (structured/top-down programming), even if they are called only once.

The following is an example of structuring, reuse, and clarifying.

If the task was to draw multiple snowmen, the solution would probably look something like this with our current know-how.

#
using Jypeli;

/// <summary>
/// Draws a snowman.
/// </summary>
public class Snowman : PhysicsGame
{

  /// <summary>
  /// The main program starts the game.
  /// </summary>
  public static void Main()
  {
    using (Snowman game = new Snowman())
    {
      game.Run();
    }
  }

  /// <summary>
  /// A subroutine for
  /// drawing circles.
  /// </summary>
  public override void Begin()
  {
    Camera.ZoomToLevel();
    Level.Background.Color = Color.Black;

    PhysicsObject p1, p2, p3;

    // First snowman
    p1 = new PhysicsObject(2 * 100, 2 * 100, Shape.Circle);
    p1.Y = Level.Bottom + 200;
    Add(p1);

    p2 = new PhysicsObject(2 * 50, 2 * 50, Shape.Circle);
    p2.Y = p1.Y + 100 + 50;
    Add(p2);

    p3 = new PhysicsObject(2 * 30, 2 * 30, Shape.Circle);
    p3.Y = p2.Y + 50 + 30;
    Add(p3);

    // 2nd snowman
    p1 = new PhysicsObject(2 * 100, 2 * 100, Shape.Circle);
    p1.X = 200;
    p1.Y = Level.Bottom + 300;
    Add(p1);

    p2 = new PhysicsObject(2 * 50, 2 * 50, Shape.Circle);
    p2.X = 200;
    p2.Y = p1.Y + 100 + 50;
    Add(p2);

    p3 = new PhysicsObject(2 * 30, 2 * 30, Shape.Circle);
    p3.X = 200;
    p3.Y = p2.Y + 50 + 30;
    Add(p3);
  }
}

 

We notice that the lines of code for drawing the first and the second snowman are almost identical. In fact, the only difference is the coordinates of the snowmen. First, we try to make the lines of code for drawing both the snowmen exactly identical.

First, we could write the code so that the centre of the bottom snowball in the snowman is saved as variables x and y. With the help of these coordinates we can then calculate the position of the other snowballs. Let’s also define p1, p2, and p3 as PhysicsObjects. Line numbers have been left out for clarity. At the end of this chapter, line numbering is included when we present the finished program. Remember: we can include the this self-reference when referring to the object’s own attributes.

double x, y;
PhysicsObject p1, p2, p3;

// Let's make the first snowman
x = 0; y = Level.Bottom + 200;
p1 = new PhysicsObject(2*100, 2*100, Shape.Circle);
p1.X = x;
p1.Y = y;
this.Add(p1);

p2 = new PhysicsObject(2 * 50, 2 * 50, Shape.Circle);
p2.X = x;
p2.Y = y + 100 + 50; // y + radius of 1st snowball + radius of 2nd ball
this.Add(p2);

p3 = new PhysicsObject(2 * 30, 2 * 30, Shape.Circle);
p3.X = x;
p3.Y = y + 100 + 2 * 50 + 30; // y + radius of 1st ball + diameter of 2nd ball + radius of 3rd ball
this.Add(p3);

Respectively, we only need to set the correct values of x and y for the second snowman.

// Let's make the second snowman
x = 200; y = Level.Bottom + 300;
p1 = new PhysicsObject(2 * 100, 2 * 100, Shape.Circle);
p1.X = x;
p1.Y = y;
this.Add(p1);

p2 = new PhysicsObject(2 * 50, 2 * 50, Shape.Circle);
p2.X = x;
p2.Y = y + 100 + 50;
this.Add(p2);

p3 = new PhysicsObject(2 * 30, 2 * 30, Shape.Circle);
p3.X = x;
p3.Y = y + 100 + 2*50 + 30;
this.Add(p3);

Let’s inspect the changes in detail.

double x, y;

The line above introduces two variables, the type of which are floating point numbers. A floating point number is a way of presenting real numbers in computers. In C# each variable has to have a type, and one of types of the floating point number is double. Variables and their types will be discussed in more detail in chapter 7.

Floating point (number) = one way of presenting real numbers in computers. More information about floating point numbers can be found in chapter 26.

x = 0; y = Level.Bottom + 200;

The line above contains two statements. The first one sets the value of x to be 0 and the second one sets the value of yto be 50 (if, for example, Level.Bottom happens to be -150). Now we can use these variables for the calculations related to the snowballs.

x = 200; y = Level.Bottom + 300;

Respectively, the line above sets new values for the variables which are used for calculating the positions of snowballs in the next snowman. Notice that the y-cooordinate receives a negative value, in which case the centre of the bottom snowball in the snowman descends below the middle level of the screen.

Now, the x-coordinate is set to be variable x and respectively the y-coordinate is set to be variable y, and the positions of the other snowballs are calculated based on the coordinates of the first snowball.

#

Image 4: Two snowmen

After these changes, the drawing process of both snowmen is performed with exactly the same code from line x= onwards.

Drawing new snowmen is now somewhat easier because all we have to do is just indicate the position of the new snowman before drawing it, and the drawing itself is simply a matter of copying and pasting the code. However, if we must copy-paste our code, we should consider if it’s reasonable to do so.

In the case of drawing two snowmen, copying and pasting is still manageable without increasing the amount of code uncontrollably, but what if we have to draw 10 or 100 snowmen? How many lines of code would the program then contain? When an almost identical strip of code is appears in several places, it is usually necessary to form a subroutine of it. Pasting the same code into several places would only increase the amount of code and complicate both understanding and testing the program.

In addition, if the repeated code contained errors, corrections would also have to made in several places. One of the criteria for how good a program is is that if something needs to be changed, would these changes be done in one place only (good) or several places at a time (bad).

6.1 Subroutine calls

#
Making a method without parameters (in Finnish) Lecture 2 (4m56s)
#
BuildSnowman-method with parameters Lecture 2 (8m39s)
A show for function call Luento 3, 2018s (33m38s)

We want to make a subroutine that draws a snowman in a specific point. Just like methods, subroutines also receive necessary information with the help of parameters. Parameters should only convey the most minimal amount of information in order to perform the task of the subroutine.

Let’s agree that our subroutine always draws a snowman of the same size in a specified point (position). What is the necessary information that the subroutine needs to draw a snowman?

The subroutine needs the information about which point the snowman should be drawn in. For this, we will give the centre point of the bottom snowball in the snowman as a parameter. The positions of the other snowballs can be calculated with the help of this centre point. Additionally, we need one parameter of the type Game so that our subroutine can be called from other programs as well. These parameters are anough to draw a snowman.

When a subroutine is used in a program, we say that we are calling a subroutine. The call can be performed by writing the name of the subroutine and giving it its parameters. The only difference between a subroutine call and a method call is that a method is always related to a certain object. For example, the ball object p1 could be removed from the game level by calling the method Destroy(); this call would be written as follows:

p1.Destroy();
#

Run the program first. This should draw a square and a circle. Then add a line at the end of the program which destroys the circle by calling the Destroy method. Then run the program again. Destroy the square as well.

//
        Level.Background.Color = Color.Black;
        PhysicsObject square = new PhysicsObject(200, 100, Shape.Rectangle);
        square.X = -200;
        square.Color = Color.Blue;
        Add(square, 0);

        PhysicsObject circle = new PhysicsObject(200, 200, Shape.Circle);
        circle.X = square.X + 250;
        circle.Color = Color.Yellow;
        Add(circle, 0);

 

In other words, when we call methods, we first need to write the name of the object for which we call the method, then a full stop (.), and finally, the name of the method we are calling. The parentheses naturally contain the necessary parameters of the method. The Destroy-method above does not receive any parameters.

6.1.1 Coding subroutine calls

Let’s decide that the name of the subroutine is DrawSnowman. Let’s also agree that the first parameter of the subroutine is the game in which the snowman is drawn (by writing this). The second parameter is the x-coordinate of the centre point of the bottom snowball in the snowman and the third parameter is the y-coordinate of the centre point of the bottom snowball. Now we can draw a snowman with the centre of the bottom snowball at the point (0, Level.Bottom + 200) with the following call:

DrawSnowman(this, 0, Level.Bottom + 200);

The call can also start with the name of the class in which the subroutine is located. With this call the subroutine can also be called from other classes, because the access modifier of the Snowmen class is public.

Snowmen.DrawSnowman(this, 0, Level.Bottom + 200);

Although this format resembles a method call quite a lot, there is a clear difference. When we call a method, its action is always performed for a certain object, like p1.Destroy() destroys only the ball to which the object p1 refers. Of course, there can be multiple ball objects in the program (like in our example). However, the subroutine call below simply uses the DrawSnowman subroutine which is located in the class Snowmen.

If we had coded the subroutine itself already, Begin would now draw us two snowmen.

/// <summary>
/// Calls the subroutine DrawSnowman
/// with the necessary parameters.
/// </summary>
public override void Begin()
{
  Camera.ZoomToLevel();
  Level.Background.Color = Color.Black;

  DrawSnowman(this, 0, Level.Bottom + 200);
  DrawSnowman(this, 200, Level.Bottom + 300);
}

Of course, because the subroutine DrawSnowman doesn’t exist yet, the program is not functional yet. In order to get the calls to work we have to implement the subroutine itself.

It is often wise to progress specifically in this order when implementing a program: first, formulate the subroutine call, write the call in the place it belongs, and only then implement the code of the subroutine itself.

More information about calling subroutines can be found in the document Calling subroutines (currently only available in Finnish):

6.2 Coding subroutines

Before we start to code the functionality of the subroutine DrawSnowman, we need to introduce or declare the subroutine. Let’s write the declaration of the subroutine that was already called in the previous section.

We will add the frame of the subroutine into our program. We will also document the subroutine right away.

/// <summary>
/// Calls the subroutine DrawSnowman
/// with the necessary parameters.
/// </summary>
public override void Begin()
{
  Camera.ZoomToLevel();
  Level.Background.Color = Color.Black;

  DrawSnowman(this, 0, Level.Bottom + 200);
  DrawSnowman(this, 200, Level.Bottom + 300);
}

/// <summary>
/// A subroutine that draws a
/// snowman in the specified position.
/// </summary>
/// <param name="game">The game that contains the snowman</param>
/// <param name="x">The x-coordinate of the centre point of the bottom snowball in the snowman</param>
/// <param name="y">The y-coordinate of the centre point of the bottom snowball in the snowman</param>
public static void DrawSnowman(Game game, double x, double y)
{
}

The image below clarifies the connection between the subroutine call and the subroutine declaration and its complementary parameters.

#

Image 5: Subroutine call and the complementary parameters of the subroutine.

The first line of the subroutine implementation

public static void DrawSnowman(Game game, double x, double y)

is called the subroutine header or introductory line. The first part of the header (the access modifier) defines the visibility of the subroutine as public. When a subroutine is public, it can be called (or used) in other classes as well.

This subroutine is also defined static. The implementation of a static subroutine cannot use the this self-reference, because it is not associated with any object. However, its benefit is that then the subroutine can be called from any part of the program and it is not (in this case) dependent on our game; also other games can make calls to this subroutine. If we would not define this subroutine as static, it would be a method, i.e. an object function (see section 8.5.).

A static subroutine needs to be able to perform all of its functions only with the help of the information provided to it as parameters.

However, a static subroutine can also use static (global) variables and constants. Use of static variables is not recommended, but use of constants is possible.

The subroutine has the return value type void, which means that the subroutine does not return any value. As a matter of fact, a subroutine could return a value that is needed in the program when the subroutine is finished. These types of subroutines will be discussed in chapter 9.

After the definition ´void´, we named the subroutine DrawSnowman.

Note! In C#, the names of subroutines are usually capitalised.

Note! The names of subroutines (and methods) should be verbs or clauses that express an action, for example CreateBall, Move, RanIntoAnObstacle.

The parameters of the subroutine are declared within parentheses after the name of the subroutine. Before each parameter we also need to declare the data type of the parameter. In this subroutine, the provided parameters were the x- and y-coordinates of the bottom snowball. The data type of both of these is double, so the complementary parameters in the subroutine also need to be of the type double. We will also give them descriptive names x and y.

To recap, here are the words in the introductory line of the subroutine:

public static void DrawSnowman(Game game, double x, double y)
Word Meaning
public subroutine is public and it can be called by anyone
static subroutine only needs information received via parameters
void subroutine does not return any value
DrawSnowman the name of the subroutine (can be anything you want; note the naming conventions though)
Game data type of the game (note: this is capitalised because it’s the name of an object (class))
game name of the first parameter (can be anything you want)
double data type of the x-coordinate
x name of the second parameter, the x-coordinate (can be anything you want)
double data type of the y-coordinate
y name of the third parameter, the y-coordinate (can be anything you want)

Because we decided to call this subroutine with three actual parameters in the following way:

DrawSnowman(this, 200, Level.Bottom + 300);

the introductory line of the subroutine needs to declare three formal parameters of the same type in the same order. Of course, 200 is an integer, but an integer can be assigned to a real number, so for the sake of general use, x and y are declared as real numbers in this case. Thanks to this, the subroutine can also be called as follows:

DrawSnowman(this, 10.3, 200.723);

More information about data types can be found in section 7.2. and chapter 8.

Parameters are separated by commas in both the subroutine call (actual parameters) and the intorductory line of the subroutine (formal parameters).

Note! The names of formal parameters in a subroutine do not need to be the same as in the parameter call. The names should, however, be as descriptive as possible.

Note! The parameter types do not need to be exactly the same as long as each formal parameter is assignment-compatible with the complementary parameter in the subroutine call. More examples of functions can be found in the document Coding subroutines (currently only available in Finnish):

https://tim.jyu.fi/view/kurssit/tie/ohj1/materiaali/aliohjelmienKirjoittaminen.

In fact, because this in the call above is of the type Snowmen, inherited from the class PhysicsGame but because PhysicsGame is inherited from the regular game class Game, variables of both the type Snowmen and PhysicsGame can be assigned to a variable of the type Game. Of course, the introductory line of the subroutine could also declare the variable type of game to be SnowMen or PhysicsGame, but then the subroutine would not be able to draw a snowman into a game of the type Game (inherited from the class Game). In other words, the generalisation here is similar to assigning 200 (an integer) into a variable of the real number type (double).

So, the subroutine call and declaration have a very strong connection. The information provided in the subroutine call (actual parameters) are “assigned” to the complementary parameters in the introductory line of the subroutine (formal parameters) with each call. In other words, broadly the following actions are performed when calling a subroutine.

the game in the subroutine = this;
x in the subroutine = 200;
y in the subroutine = Level.Bottom + 300;

Now we can run our program. It works (it runs), but of course it doesn’t draw any snowmen, and it shouldn’t because the subroutine we just created is “empty”. Between the braces, let’s add the code that is necessary to draw snowballs.

A small change to the earlier version is needed, however. Lines that add the snowballs to the field are changed as follows

game.Add(...);

in which the dots are replaced by the name of the ball object. This is because, in fact, we originally should have always written:

this.Add(p1);
this.Add(p2);
etc.

In the original snowman program we called the method Begin of the Snowman class, which was meant to declare that the snowballs need to be added to this (this) specific game (game object, which is an instance of the Snowman class). In many object-oriented languages it is possible to either include or leave out the self-reference this when referring to the object’s own methods (like Add here) or attributes. Everyone can chooseone’s own style, but in this material this is usually left out. Similarly, DrawSnowman is not the subroutine of a particular object (this is caused by the declaration static), which is why it receives the information about which game includes the snowman it draws as a parameter. In our example, we particularly relayed the parameter this to it. This is why the subroutine call in our example

game.Add(p1);

is specifically

this.Add(p1);    

Finally, the complete Begin method and DrawSnowman subroutine:

#
//
    /// <summary>
    /// Calls the DrawSnowman subroutine
    /// with necessary parameters.
    /// </summary>
    public override void Begin()
    {
        Camera.ZoomToLevel();
        Level.Background.Color = Color.Black;

        DrawSnowman(this, 0, Level.Bottom + 200);
        DrawSnowman(this, 200, Level.Bottom + 300);
    }

    /// <summary>
    /// A subroutine that draws a
    /// snowman in the specified position.
    /// </summary>
    /// <param name="game">The game that contains the snowman</param>
    /// <param name="x">The x-coordinate of the centre point of the bottom snowball in the snowman</param>
    /// <param name="y">The y-coordinate of the centre point of the bottom snowball in the snowman</param>
    public static void DrawSnowman(Game game, double x, double y)
    {

        PhysicsObject p1, p2, p3;
        p1 = new PhysicsObject(2 * 100, 2 * 100, Shape.Circle);
        p1.X = x;
        p1.Y = y;
        game.Add(p1);

        p2 = new PhysicsObject(2 * 50, 2 * 50, Shape.Circle);
        p2.X = x;
        p2.Y = p1.Y + 100 + 50;
        game.Add(p2);

        p3 = new PhysicsObject(2 * 30, 2 * 30, Shape.Circle);
        p3.X = x;
        p3.Y = p2.Y + 50 + 30;
        game.Add(p3);
    }

 

In C# (like in many other languages) it doesn’t matter which one is coded first, the the main program (in this case Begin) or the subroutines it calls (in this case DrawSnowman). The most important thing is that they form entities (i.e. they are blocks enclosed within braces {}).

Subroutines are not executed in the order they appear in the code but in the order they are called. The execution of a program always starts from the Main program, and in the case of Jypeli the main program calls the Begin method which can call other subroutines, which can also call other subroutines. When the subroutine is executed, the execution of the program returns to the part where the subroutine was called.

When we form the code for the subroutine functionality, we use the names we gave to parameters. The coordinates of the centre of the bottom circle are provided in the parameters x and y, but the centres of the other circles need to be calculated with the help of the coordinates of the bottom circle. This is done exactly like in the example earlier. In fact, if we compare the contents of the subroutine with the contents of the previous example, they are exactly the same.

In C#, it is customary to capitalise the names of subroutines and methods and each word within the name. This style of writing is called PascalCasing. Variables are not capitalised, but each following word within the name of the variable is capitalised: for example double speedOfCar. This style of writing is called camelCasing. More about the naming conventions of C# here:

Let’s take a look at what happens in the subroutine call.

DrawSnowman(this, 0, Level.Bottom + 200);

The call above assigns the value this (i.e. the game in question) to the variable game, the value 0 to the variable x (an integer can be assigned to a floating point number), and the value Level.Bottom + 200 to the variable y. Of course, we could also assign any other floating point number to x and y.

The execution of a subroutine call first calculates the value of each expression in the call and then these calculated values are assigned to the corresponding parameters in the subroutine in the order designated in the call. For this reason the corresponding parameters must be assigment-compatible with the expressions in the call. The example call has simple expressions: the name of a variable (this), an integer (0), and a real number (Level.Bottom + 200). However, they could also be more complex, for example like this:

DrawSnowman(this, 22.7+sin(2.4), 80.1-Math.PI);

An expression is the combination of values, arithmetic operations, and subroutines (or methods), which is evaluated into one value.

#

Image 6: an example of an expression (Lauseke) in a statement (Lause)

Because we defined the type of coordinates as double, we could assign any other types of decimal numbers as parameters as well. Remember that in C#, the decimal number constant uses the dot (.) as decimal separator, i.e. to separate the integer from the decimal part.

6.2.1 Complete program

In its entirety the program looks like this:

#
using Jypeli;


/// @author  Antti-Jussi Lakanen, Vesa Lappalainen
/// @version 22.8.2012
///
/// <summary>
/// Practice the use of subroutines by drawing snowmen.
/// </summary>
public class Snowmen : PhysicsGame
{

    /// <summary>
    /// The main program starts the "game".
    /// </summary>
    public static void Main()
    {
        using (Snowmen game = new Snowmen())
        {
            game.Run();
        }
    }

    /// <summary>
    /// Calls the DrawSnowman subroutine
    /// with necessary parameters.
    /// </summary>
    public override void Begin()
    {
        Camera.ZoomToLevel();
        Level.Background.Color = Color.Black;

        DrawSnowman(this, 0, Level.Bottom + 200);
        DrawSnowman(this, 200, Level.Bottom + 300);
    }

    /// <summary>
    /// A subroutine that draws a
    /// snowman in the specified position.
    /// </summary>
    /// <param name="game">The game that contains the snowman</param>
    /// <param name="x">The x-coordinate of the centre point of the bottom snowball in the snowman</param>
    /// <param name="y">The y-coordinate of the centre point of the bottom snowball in the snowman</param>
    public static void DrawSnowman(Game game, double x, double y)
    {

        PhysicsObject p1, p2, p3;
        p1 = new PhysicsObject(2 * 100, 2 * 100, Shape.Circle);
        p1.X = x;
        p1.Y = y;
        game.Add(p1);

        p2 = new PhysicsObject(2 * 50, 2 * 50, Shape.Circle);
        p2.X = x;
        p2.Y = p1.Y + 100 + 50;
        game.Add(p2);

        p3 = new PhysicsObject(2 * 30, 2 * 30, Shape.Circle);
        p3.X = x;
        p3.Y = p2.Y + 50 + 30;
        game.Add(p3);
    }
}

 

When calling a subroutine the execution of the program jumps to the first line of the called subroutine immediately after assigning parameters and start executing the subroutine with the parameters defined in the call. When the exeution reaches the last line of the subroutine, the execution continues from the next statement after the subroutine call. In our example, when the first snowman has been drawn, the execution continues from the semicolon at the end of the first statement and then the main program continues with the call to draw the other snowman.

If we now want to draw more snowmen, each new snowman would only add one line to the code.

Note! The use of subroutines makes the program code clearer and more readable, which is why subroutines should be added even if they were called only once. A well-formed subroutine might be called on other occasions as well.

#

Task 6.1. More snowmen

Add two more snowmen to the program

//
        //PiirraLumiukko = DrawSnowman
        PiirraLumiukko(this, 0, Level.Bottom + 200);
        PiirraLumiukko(this, 200, Level.Bottom + 300);

 

(käänt.) saisko esimerkin Snowman.cs, jotta voisi kutsua aliohjelmaa DrawSnowman?

28 Sep 17
#

You can also give the names of the parameters, which allows changing the order of parameters in the call. Add two more snowmen to the program. Try different ways of providing parameters with names included. Try to add Game. in front of the DrawSnowman call as well. Why Game.?

//
        //PiirraLumiukko = DrawSnowman
        PiirraLumiukko(peli:this, y:Level.Bottom + 200, x:0);
        PiirraLumiukko(this, x:200, y:Level.Bottom + 300);

 

In C# subroutines and functions can be overloaded with different parameters. This means that the program can contain multiple subroutines with the same name as long as they have the different number (or different types) of parameters. More in section 6.5..

#
More information about overloading on video (in Finnish) Overloading Snowman (12m54s)
#

Task 6.2. Arrange correctly

Arrange the block to make this a functional program. Place the main program before other subroutines.

//
public class Print
{
   public static void Main()
   {
       PrintNumbers(0, -99);
   }
   public static void PrintNumbers(double p1, double p2)
   {
       System.Console.WriteLine(p1 + " " +  p2);
   }
}

 

6.3 Documenting subroutines

According to good coding conventions each subroutine should contain a documentation comment. The documentation comment for a subroutine should contain at least the following information: a short description of the functionality of the program, a description of all the parameters, and an explanation of the return value. This information is described in tags in the following way:

  • The documentation comment starts with the summary tags; the text between the tags is a short and clear description of the functionality of the subroutine
  • Each parameter is described between the param tags, and
  • Each return value between the returns tags

The documentation comments for the DrawSnowman subroutine are on lines 36-42 in our previous example.

36 /// <summary>
37 /// A subroutine that draws a
38 /// snowman in the specified position.
39 /// </summary>
40 /// <param name="game">The game that contains the snowman</param>
41 /// <param name="x">The x-coordinate of the centre point of the bottom snowball in the snowman</param>
42 /// <param name="y">The y-coordinate of the centre point of the bottom snowball in the snowman</param>

You can try documentation in the previous complete Snowman example by clicking the Document link. Then try to different links in the documentation to see what’s behind them. The same can be seen in the image below.

The HTML page produced from this class by the Doxygen tool (see: http://en.wikipedia.org/wiki/Doxygen) would look like this:

#

Image 7: Part of the documentation of the Snowmen class (documented in Finnish).

The documentation shows all the subroutines and methods in the class. Notice that Doxygen names both subroutines and methods as member functions. As said, naming conventions vary in literature, and in this case the naming convention resembles the one used in C++. However, member functions are called subroutines and methods on this course.

The details of each subroutine and method can be found in the section Detailed Description. The documentation of the DrawSnowman subroutine with its parameters can be seen at the bottom of the image.

6.3.1 Note

All of the information needed in the DrawSnowman subroutine was relayed with the help of the parameters and no external information was needed during the execution of the subroutine. This is typical and usually also a desirable feature of subroutines. In these cases, the subroutine is defined as static.

6.4 Subroutines, methods, and functions

As you may have noted, subroutines and methods have a lot in common. Many sources also call subroutines methods. In these cases subroutines are separated from object methods by calling them static methods. However, in this material, methods refer to object functions only. In Jypeli documentation, we can study static methods of the RandomGen class which generate random number for example. A single ball was removed with the method Destroy, which is an object function.

Ww talk about subroutines on this course because the term is used in many other programming languages as well. This course is primarily a programming course that uses C#. Our main goal is therefore to learn how to program and we use the C# language as a tool for learning it.

Our subroutine DrawSnowman did not return any value (void). Subroutines (or methods) that return a value can also be called functions more specifically.

Different names are used for subroutines and methods in different languages. For example, in the C++ language, both subroutines and methods are called functions. In C++ methods are more specifically called member functions, like Doxygen did in the case of C# as well.

Let’s revise the differences of subroutines, functions, and methods in brief.

Subroutine: a general name for any kind of subroutine, function, or method. A word subroutine does not specicy the number of parameters or return value. In void-type subroutine there can be a return-sentence, but without a value. Then the only meaning for return is to jump out of from the subroutine.

In some languages, for example C++, all different types of subroutines are called functions. In Java literature all subroutines are usually called methods.

On this course, the gereral name subroutine is used when we don’t want to emphasise that the block is especially a function or a method. Next, let’s specify these concepts.

Function: a subroutine that returns a result value, for example the average of two numbers. According to this definition, a function always contains at least one return statement followed by an expression, for example return (a+b)/2.0; Even a void subroutine (i.e., a subroutine that doesn’t return a value) can contain a return statement, but it can’t be followed by an expression. A function should usually be static.

Method: a subroutine that uses the information of an object for performing a task. Methods are used on this course (for example string.IndexOf), but not coded except for methods of the game class (for example Begin). At the end of the course, some can also potentially make a new class and then write own methods for it. In practice, a method uses the this reference and for this reason it cannot be static.

A method can also return a value, similarly to a function, or it can be void, i.e. not return a value, similarly to a subroutine.

#

6.4.1 Coding subroutines

When coding subroutines, it would be best to progress like this (as long as we learn how to test, TDD, Test Driven Development):

  1. Separate the problem into parts.
  2. Come up with a descriptive subroutine call that starts the solving of a certain problem.
  3. Write the call line of the subroutine and think what parameters it requires.
  4. Write (first manually, later generate automatically) the introductory line (the header) of the subroutine.
    • think about if it needs the words public,static
    • is the return type of the subroutine void or something else?
    • the name of the subroutine
    • the same number of parameters as in the subroutine call.
    • assignment-compatible types of parameters to the call.
  5. Make a syntactically correct stub of the subroutine that compiles, for example a function subroutine needs to a have a return statement that return an expression (for example one number) that is of the same type (or is automatically turned into the same) as the type of the function.
  6. Document the subroutine (don’t mention where it was called, it is not important here)
  7. Write the tests (TDD) - see the note about testing below
  8. Run the tests (they should fail == you will see a red bar).
  9. Make the subroutine functional.
  10. Run the tests (repeat phases 8-10 until it works = you will see a green bar).
  11. Move onto the next subroutine.

Read more in the document Writing subroutines (in Finnish).

The instructions above are the common “recipe” for coding subroutines. It includes tests, but with the information provided on this course we can only test the functions that are discussed later in this material in chapter Return value of a subroutine. In other words, the “recipe” above cannot be used properly before we learn more about functions and testing. Subroutines that print out something cannot be tested with the information from this course.

#

This program has a complete main program and the frame for a subroutine. However, the subroutine doesn't do anything yet. Make the subroutine print the text "Hello World"

//
public class HelloWorld
{
    public static void Main()
    {

        PrintHelloWorld();

    }

    ///<summary>
    ///Prints "Hello World!"
    ///</summary>
    public static void PrintHelloWorld() {

      // text here
    }
}

 

#

Tehtävä 6.2

Click 'Show all code' in order to see the subroutines in this program. How would you print "Hello World!" by using only subroutine calls? The first subroutine call is provided already.

//
        PrintHe();

 

Once again! Dont forget to use Tulosta instead of Print in subroutine calls, or it won’ work

07 Feb 21

Of course, subroutines like the ones above are not very efficient and it would be more sensible to give the subroutine the text it should print as a parameter.

#

Task 6.3.

Complete the Dice.cs program below to function in the way that the comments describe.

using System;
using Jypeli;

/// <summary>
/// The program draws six dots inside a square so that they form an object that
/// looks like dice with the value 6 showing.
/// </summary>
public class Game : PhysicsGame
{
    /// <summary>
    /// The content that shows on the screen.
    /// </summary>
    public override void Begin()
    {
       Camera.ZoomToLevel();
       Level.Background.Color = Color.Black;
       double size = 500;
       PhysicsObject square = new PhysicsObject(size, size, Shape.Rectangle);
       Add(square);
       DrawSquare(this, 0, 100);
       DrawSquare(this, 120, 100);
       // Complete ...
    }

   /// <summary>
   /// Draws a square with sides of size 80.Piirtää pallon, jonka sivun pituus on 80.
   /// </summary>
   /// <param name="game">Peli, johon neliö piirretään</param>
   /// <param name="x">The x-coordinate of the centre of the square.</param>
   /// <param name="y">The y-coordinate of the centre of the square.</param>
   public static void DrawSquare(Game game, double x, double y)
   {
       PhysicsObject p1 = new PhysicsObject(80, 80, Shape.Circle);
       // Complete
       game.Add(p1,1);
   }
}

 

(käänt.) suomenkielisessä esimerkissä oli vääriä sanoja (pallo neliön sijaan)

30 Sep 17
The result should look something like this
The result should look something like this

6.4.2 Task: Terminology

/// <summary>
/// Calls the DrawSnowman subroutine
/// with necessary parameters.
/// </summary>
public override void Begin()
{
    Camera.ZoomToLevel();
    Level.Background.Color = Color.Black;

    DrawSnowman(this, 0, Level.Bottom + 200);
    DrawSnowman(this, 200, Level.Bottom + 300);
}
#
Task: Terminology

Which of the following statements concerning the program above are true?

#

6.5 Overloading subroutines

In C# subroutines and functions can be overloaded with parameters. This means that a program can contain multiple subroutines with the same name that have a different number or types of parameters. This can be utilised so that the function that takes in more parameters can do more or do the same operation with more details than a function that takes in less parameters.

6.5.1 A simple example

First, let’s take as simple an example as possible about overloading. The function in case sums numbers.

First, we make a function that returns the sum of two numbers.

public static double Sum(double a, double b)
{
  return a + b;
}

This can be called from the Main program by writing

double sum = Sum(5, 10.5);

Then we say hey, we also need a function that can sum three numbers, and we want to call it by writing

double sum = Sum(5, 10.5, 30.9);

We write a function with the same name but in the introductory line (function signature) we give three parameters instead of two. We will implement the function immediately as well.

public static double Sum(double a, double b, double c)
{
  return a + b + c;
}

But now we notice that we have almost similar code in two function. We will change the first function so that it calls the first function (that can do less) calls the second function (that can do more). We will provide 0 as the third number (third parameter).

public static double Sum(double a, double b)
{
  return Sum(a, b, 0);
}

With this example we learned what overloading means. The next example brings out the benefits of overloading better.

6.5.2 Standard-size snowman vs. size of snowman as parameters

We can create a standard-size snowman with the following subroutine.


/// <summary>
/// A subroutine that draws a standard-size
/// snowman in the specified position.
/// </summary>
/// <param name="game">The game that contains the snowman</param>
/// <param name="x">The x-coordinate of the centre point of the bottom snowball in the snowman</param>
/// <param name="y">The y-coordinate of the centre point of the bottom snowball in the snowman</param>
public static void DrawSnowman(Game game, double x, double y)
{

    PhysicsObject bottomcircle, middlecircle, topcircle;
    bottomcircle = new PhysicsObject(2 * 100, 2 * 100, Shape.Circle);
    bottomcircle.X = x;
    bottomcircle.Y = y;
    game.Add(bottomcircle);

    middlecircle = new PhysicsObject(2 * 50, 2 * 50, Shape.Circle);
    middlecircle.X = x;
    middlecircle.Y = bottomcircle.Y + 100 + 50;
    game.Add(middlecircle);

    topcircle = new PhysicsObject(2 * 30, 2 * 30, Shape.Circle);
    topcircle.X = x;
    topcircle.Y = middlecircle.Y + 50 + 30;
    game.Add(topcircle);
}

We can call the subroutine in Begin like this for example.

DrawSnowman(this, 0, Level.Bottom + 200.0);

But what if we want to draw snowmen of different size sometimes? In other words, it would be enough if the DrawSnowman subroutine drew a snowman of different size in addition to a standard size snowman. The calls in Begin could look like this.

// Calling the drawing of a standard-size snowman (size of bottom snowball = 2 * 100)
Snowman(this, -200, Level.Bottom + 300.0);

// Using the subroutine with the same name  
// for drawing a smaller snowman (size of bottom snowball = 2 * 50)
Snowman(this, 0, Level.Bottom + 200.0, 50.0);

But now Visual Studio shows the error

No overload for method 'PiirraLumiukko' takes 4 arguments.

So now we will write a new subroutine called DrawSnowman (yes, the same name) but the in addition to the game and position parameters we also provide the radius of the bottom snowball.

public static void DrawSnowman(Game game, double x, double y, double radius)
{
 // code will be inserted soon...
}

Move the code from the original subroutine to this new subroutine and set the radius of snowballs to be dependent on the radius provided as a parameter. Additionally, set the positions of the middle and top snowball to be dependent on the size of the snowballs! The new (four-parameter) subroutine would look like this.

public static void DrawSnowman(Game game, double x, double y, double radius)
{
    PhysicsObject bottomcircle, middlecircle, topcircle;
    bottomcircle = new PhysicsObject(2 * radius, 2 * radius, Shape.Circle);
    bottomcircle.X = x;
    bottomcircle.Y = y;
    game.Add(bottomcircle);

    // size of the middlecircle is 0.5 * radius
    middlecircle = new PhysicsObject(2 * 0.5 * radius, 2 * 0.5 * radius, Shape.Circle);
    middlecircle.X = x;
    middlecircle.Y = bottomcircle.Y + bottomcircle.Height / 2 + middlecircle.Height / 2;
    game.Add(middlecircle);

    // size of the topcircle is 0.3 * radius
    topcircle = new PhysicsObject(2 * 0.3 * radius, 2 * 0.3 * radius, Shape.Circle);
    topcircle.X = x;
    topcircle.Y = middlecircle.Y + middlecircle.Height / 2 + topcircle.Height / 2;
    game.Add(topcircle);
}

We can now call this “version” that does more from the three-parameter DrawSnowman subroutine, which saves us from copy-pasting code.

public static void DrawSnowman(Game game, double x, double y)
{
    DrawSnowman(game, x, y, 100);
}
Snowmen drawn with two subroutines together.
Snowmen drawn with two subroutines together.
#

The complete program code now (names in Finnish)

using System;
using System.Collections.Generic;
using Jypeli;
using Jypeli.Assets;
using Jypeli.Controls;
using Jypeli.Effects;
using Jypeli.Widgets;

/// @author Antti-Jussi Lakanen
/// @version 30.1.2014
///
/// <summary>
/// Aliohjelmien kuormittaminen
/// </summary>
public class Kuormittaminen : PhysicsGame
{
    /// <summary>
    /// Kutsutaan PiirraLumiukko-aliohjelmaa kahdella eri tavalla.
    /// Ensimmäisessä ei anneta parametrina kokoa, jolloin tulee "vakiokokoinen" lumiukko.
    /// Toisessa tavassa annetaan parametrina haluttu koko.
    /// </summary>
    public override void Begin()
    {
        Level.Background.Color = Color.Black;
        PiirraLumiukko(this, -200, Level.Bottom + 300.0);
        PiirraLumiukko(this, 0, Level.Bottom + 200.0, 50.0);

        PhoneBackButton.Listen(ConfirmExit, "Lopeta peli");
        Keyboard.Listen(Key.Escape, ButtonState.Pressed, ConfirmExit, "Lopeta peli");
    }

    /// <summary>
    /// Aliohjelma piirtää vakiokokoisen lumiukon
    /// annettuun paikkaan.
    /// </summary>
    /// <param name="peli">Peli, johon lumiukko tehdään.</param>
    /// <param name="x">Lumiukon alimman pallon x-koordinaatti.</param>
    /// <param name="y">Lumiukon alimman pallon y-koordinaatti.</param>
    public static void PiirraLumiukko(Game peli, double x, double y)
    {
        PiirraLumiukko(peli, x, y, 100);
    }

    /// <summary>
    /// Aliohjelma piirtää annetun kokoisen lumiukon
    /// annettuun paikkaan
    /// </summary>
    /// <param name="peli">Peli, johon lumiukko tehdään.</param>
    /// <param name="x">Lumiukon alimman pallon x-koordinaatti.</param>
    /// <param name="y">Lumiukon alimman pallon y-koordinaatti.</param>
    /// <param name="sade"></param>
    public static void PiirraLumiukko(Game peli, double x, double y, double sade)
    {
        PhysicsObject alapallo, keskipallo, ylapallo;
        alapallo = new PhysicsObject(2 * sade, 2 * sade, Shape.Circle);
        alapallo.X = x;
        alapallo.Y = y;
        peli.Add(alapallo);

        keskipallo = new PhysicsObject(2 * 0.5 * sade, 2 * 0.5 * sade, Shape.Circle);
        keskipallo.X = x;
        keskipallo.Y = alapallo.Y + alapallo.Height / 2 + keskipallo.Height / 2;
        peli.Add(keskipallo);

        ylapallo = new PhysicsObject(2 * 0.3 * sade, 2 * 0.3 * sade, Shape.Circle);
        ylapallo.X = x;
        ylapallo.Y = keskipallo.Y + keskipallo.Height / 2 + ylapallo.Height / 2;
        peli.Add(ylapallo);
    }
}

 

.

#

7. Variables

type name;

Variables act as data storage for different things in programs. A variable is like a small box that can store things like numbers, words, information about the user of the program, and much, much more. In procedural languages, processing information would be impossible without variables. In functional programming, things are a little different. We have already used variables, for example in the Snowman example we created the PhysicsObject-type variables p1, p2, and p3. Accordingly, the parameters (Game peli, double x, double y) in the DrawSnowman subroutine are also variables: the Game-type object variable game and the double primary-type variables x and y.

The term variable is borrowed from mathematics, but these two concepts should not be confused with each other - a variable in mathematics and a variable in programming have a slightly different meaning. You will notice this in the next sections.

The values of variables are saved in main memory or registers, but in programming languages we can give each variable a name (an identifier) in order to make it easier to handle them. The naming of variables makes programming easier, because then the programmer does not need to know the addresses of the information they need in the main memory or registers; it is enough to remember the name of the variable the programmer himself/herself named.

Because the compiler needs to reserve a memory area of the right size for the variable, the type of the variable also needs to be introduced. The type of the variable is also needed in order to know how to handle the information saved in the memory location. In order to understand the different ways in which data types are saved, we will get familiar with binary numbers etc. later. For example, the combination of eight bits, aka the byte 01000001can be interpreted as the letter A or as the natural number 65 etc.

For example, the statement Console.WriteLine(a) would not know what to print unless the type of the variable a was known. Accordingly, if you hear the word knight, you would not tell it apart from the word night unless you are given the context as well.

7.1 Defining variables

When a mathematician says that “n is equal to 1”, it means that the term (or variable) n is in some inexplicable way equal to the number 1. In mathematics, variables can be introduced as sporadically as this.

However, a programmer needs to be more specific about variables. In C#, values are assigned to variables like this:

#
   int n;
   n = 1;

 

The first line roughly translates to “chip a small piece - the size of an ìnt value - of storage space from the memory of the computer, and use the name n for it from now on”. The second line declares that “save the value 1 to the variable, which has the name n, in this way replacing whatever may already be in the storage space”.

The character = is the assignment variable, which will be discussed later.

Then what is the int in the previous example?

#

Task: Assignment

show Tauno. Assign the value 1 to the variable n by dragging the <--1 'box' on top of n. Run the program. Now the text n=1 should be printed. Then try to increment (drag +1 on top of it) and subtract (drag -1) the value of the variable and see what kind of program code Tauno produces.

 

In C# each variable must have a data type (usually type in short). The data type must be defined so that the program would know what kind of information is saved into the variable. On the other hand, the data type also needs to be defined so that the program knows how much space to reserve from the memory for the information in the variable. For example, in the case of an int variable the required space is 32 bits (4 bytes), the byte variable requires 8 bits (1 byte), and a double variable requires 64 bits (8 bytes). A variable is declared by writing the data type, followed by the name of the variable. Variable names are not capitalised in C#, but each word in the name that follows is capitalised. As was mentioned before, this naming convention is called camelCasing.

variableDataType variableName;

The int we mentioned is a data type, which stores integers. We can assign the numbers 1, 2, 3, and also 0, -1, -2 etc. in the n variable , but not the number 0.1 or the word “Hey”. However, whatever we assign to it, the variable can only have one value at a time. If a variable is assigned a new value, the previous value can no longer be accessed in any way.

A person’s age could be saved into the following variable:

int ageOfPerson;

Notice that we are not assigning any value to the variable, we are simply defining the variable type to be int and naming it.

Multiple variables of the same type can be defined at once by separating the names of variables with commas.

The data type double is used when we want to store decimal numbers.

double weight, height;

Definition can also be done separately (which is even more preferable):

double weight;
double height;

Variables can also be assigned values already when defining them. Note that the value can also be the result of an expression.

variableDataTypemuuttujanTietotyyppi variableName = CONSTANT;
variableDataType variableName = expressionThatProducesValue;
#
//
        bool isFisher = true;
        char character = 't';
        int numberOfFish = 0;
        double number1 = 0, number2 = 2.0, number3 = 3+2.4;

 

The values (or values of expressions) assigned to variables should be of the type that can be assigned to the variable in question. For example, an int variable cannot be assigned the following real number:

#
//
        int age = 2.5; // THIS WON'T COMPILE

 

but a real number can be assigned the integer

#
//
        double price = 20000;

 

Notice that the decimal separator in real numbers (such as double) is always a dot (.) and thousands are not separated.

#
Which are allowed?

Which of the following variable definitions are allowed?

7.2 Primitive data types

The data types in C# can be categorised into primitive data types (aka basic types, basic data types) and reference types. Reference types include for example the PhysicsObject type that we used earlier, like p1 etc. and the string object for saving character strings. Reference types will be discussed in more detail in chapter 8.

Different data types require a different amount of capacity in the computer’s memory. Even though computers nowadays have a lot of memory, it is extremely important to select the right type of variable for each situation. In large programs this problem is magnified very fast if we use variables that use up too much memory in relation to the operation they perform. The primitive data types in C# are listed below.

Table 1: The primitive data types in C# in size order.

During this course, the most important primitive data types are bool, char, int, and double.

In this material, it is recommended that the data type used for saving decimal numbers is always double (in some cases even decimal) even though many other sources use float as well. This is because floating point numbers, which are used in computers for handling decimal numbers, are rarely accurate values in computers. In fact, they are only accurate when they represent the combinations of any two powers, like 2.0, 7.0, 0.5, or 0.375, for example.

Most often, floating point numbers are only approximations of real numbers. For example, the number 0.1 cannot be presented accurately with bits in primitive data types. In this way, the inaccuracy only grows as the number of calculations rises. For this reason, it is always safest to use the double type, because thanks to its larger number of bits it can store more significant decimals.

In certain applications where high accuracy is necessary (for example in banking and nanophysics applications), it is recommendable to use the type with the highest possible accuracy, the decimal type. The presentation of real numbers in computers will be discussed in more detail in section 26.6.

The next example demostrates what happens when we sum two too large integers or increment the variable too much in other ways.

#

Task 7.1.

First, run the program without making any changes. Then remove 0 from one of the int variables. Run. What happened? Put the 0 back. Change the types so that the calculations turn out correctly.

//
        int number1 = 1000000000;
        int number2 = 2000000000;
        int sum = number1 + number2;
        byte b = 254;
        b++; b++;
        sbyte sb = 127;
        sb++;

 

7.3 Assigning values to variables

You can assign values to variables with the assignment operator =. Statements in which variables are assigned values are called assignment statements. It is important to notice that an assignment always happens from right to left: the value that is assigned is on the right side of the equal sign and the target variable is on the left side of the sign.

#

Show Tauno. Create a new variable, name it age and assign your age as its value. Create another variable, name it studyYears and assign a value to it. See the code that Tauno produces. Notice that it automatically defines the data type of variables as int.

 

#

Task 7.2.

Introduce the variables for the assignments below so that the program can be compiled and works. E.g. int b;

//




        x = 20.0;
        ageOfPerson = 23;
        weight = 80.5;
        height = 183.5;
        // 80.5 = weight;  // try it, this won't work!

 

Note that a decimal point is used in real number constant, not a comma.

#

Task 7.3

Define the variable types in the assignment lines.

//
        x = 20.0;
        ageOfPerson = 23;
        weight = 80.5;
        height = 183.5;
        lightyearKm = 9460730472580;
        sum = 128;
        character = '7';

 

All the variables in tasks need to be written in finnish, it won’t run.

31 Jan 21

The variable needs to be defined as a certain type before it can be assinged a value. Variables can be assigned only values of the defined data type or assignment compatible values. For example, floating point number types (float or double) can also be assigned integer values, because integers are a subset of real numbers. In the example below, we assign the value 4 to the variable number2 and on the third line we assign the value of number2 (4) to a variable named number1.

#
//
        double number1;
        int number2 = 4;
        number1 = number2;

 

This cannot be done vice versa: a value of the type double cannot be assigned to an int variable. The code below could not be compiled:

#
// THIS CODE CANNOT BE COMPILED
        int number1;
        double number2 = 4.0;
        number1 = number2;

 

If the int <- double assignment above would be absolutely necessary to make, we would have to use a variable transformation aka a typecast (try in the example above, change also 4.0 to 4.8. To start with, however, typecasting is always a bad solution.

   number1 = (int)number2;  // force the number2 to be of the type int. The number is rounded down.

When a decimal variable is initialized with a number, the number needs to be followed (before the semicolon) by m (or M). Accordingly, initialising a float variable needs to be followed by f (or F).

#
//
        decimal accountBalance = 3498.98m;
        float temperature = -4.8f;

 

Notie that a char variable is assigned a value by writing it within apostrophes like this:

#
//
        char firstLetter = 'k';

 

This sets it apart from assigning values to the string variable (discussed in detail later), which is done by placing the string within quotation marks, like this:

#
//
        string myName = "Antti-Jussi";

 

An assignment statement can also contain more complex expressions, for example arithmetic operations:

#
//
        double averageOfNumbers = (2 + 4 + 1 + 5 + 3 + 2) / 6.0;

 

An assignment statement can also contain variables.

#
//
        double lengthOfRoom = 5.40;
        double widthOfRoom = lengthOfRoom;
        double areaOfRoom = lengthOfRoom * widthOfRoom;

 

So the assigned value can be any expression that produces a value that is compatible with the variable type. By combining variable and operations, an expression can be even more “complex” than the previous examples:

#
//
        double start = 30;
        double speed = 80;
        double distance = start + (speed-10)*5 + System.Math.Sin(0.5);

 

Notice that even though we don’t need the multiplication sign on paper, programming languages require using the * sign.

C# requires assigning a value to the variable before using it. The compiler won’t compile a code in which a variable with no assigned value is used. The program below would not be compiled.

#
//
// THIS PROGRAM CANNOT BE COMPILED!!!!!!!!
public class Example
{
    public static void Main()
    {
        int age;
        System.Console.WriteLine(age);
    }
}

 

The error message looks ike this:

Example.cs(7,34): error CS0165: Use of unassigned local variable 'age'

The compiler tells that there is an attempt to use the age variable before any value is assigned to it. This is not allowed, so the attempt to compile the program ends here.

#

Try to run the program. The error 'The variable 'age' is assigned but its value is never used' happens. This will not prevent the compilation as some error messages do. By removing the comment characters // from the beginning of the line enables the use of the variable and no error message is shown.

//
public class Example
{
    public static void Main()
    {
        int age = 5;
        // System.Console.WriteLine(age);
    }
}

 

7.3.1 The target of assignment is always on the left

The variable for which the value is assigned is alawys on the left side of the statement. The right side of the = assignment operator is always some expression, the value of which is always calculated before the assignment and this value is assigned to the variable.

7.3.2 Task 7.4 assigning the value of a to b

First, answer the multiple choice questions below and then complete the task after it.

#

Vastaa aluksi alla olevaan monivalintakysymykseen ja sitten kirjoita tähän miten sijoitat a:n arvon b:hen kirjoitamalla uuden ohjelmarivin (älä siis muuta kahta olemassa olevaa). Tämän jälkeen aja ohjelma ja katso että se tulostaa 3

        int a = 3;
        int b;

 

#
Check your understanding

How would you assign the value of a to b after the two existing lines in the task below?

7.3.3 The value of the variable changes when a value is assigned to it

The value of a variable only changes when a value is assigned to it. Primitive variables are always assigned a value. If the value of a another variable is assigned to a variable, the variable receives the value which the other value has at the moment of assignment. In the following example, try changing the value of i and observe that it no longer affects the value of the sum variable:

#

Show Tauno. Assign the value of i to the sum variable. Increment the value of i by one by dragging the +1 'box' on top of it. Does the value of the variable sum change when you increment the variable i?

 

7.3.3.1 Task 7.5 Incrementing i, what the program prints

#

Don't run the program yet, answer the question below first.

//
        int i = 2;
        int sum = i;
        System.Console.Write(sum + " ");
        i += 1; // tai i++;
        System.Console.WriteLine(sum);

 

#
What changes

What will the program print?

7.4 Naming variables

The names of variables must describe the information saved in them. Usually a letter alone is a bad name for a variable, because it rarely describes the variable very well. Descriptive variable names clarify the code and reduce the necessity to write comments. A short variable name has no intrinsic value; only two decades ago it may have had because it sped up the coding process. However, with modern development environments this no longer holds true, because editors can fulfill the variable names while writing the code, so in practice the names of variables never have to written in full after defining them.

Single letter variable names can still be used when justified, if for example they have a meaning in mathematics or physics. The names x and y are good variable names for coordinates. The name l refers to length and r to radius. In a physics program the name s can be used for distance.

Note! In C#, the name of a variable cannot start with a number.

According to the coding conventions of C#, the name of a variable begins with a lowercase letter. If the name of the variable consists of multiple words, each new word in the name is capitalised, like in the example below.

int bicycleTyreSize;

In C# the name of a variable can contain scandinavian letters, but their use is not recommended, because then moving from one character encoding to another will usually cause extra problems.

Character encoding = Defines an individual code number for each character in a character set. The numeric representation of characters is usually necessary in computers. The character set defines a set of characters and their names, numbers, and some kind of description of their format. Character set and character encoding usually refer to the same thing, for example the Unicode character set contains multiple encoding methods (UTF-8, UTF-16, UTF-32). In other words, character encoding is the part in a character set that defines the numeric code value for each character. Character encoding usually causes problems when moving from a character encoding that includes scandinavian letters (ä,ö,å, …) to the 7-bit ASCII encoding that has no support for scandinavian letters. More about ASCII encoding in chapter 27.

7.4.1 C# keywords

Names of variables cannot be any of the reserved words in the used programming language, i.e. words that have another meaning and function in C#.

Table 2: C# keywords i.e. the “reserved words”.

abstract do in protected true
as double int public try
base else interface readonly typeof
bool enum internal ref uint
break event is return ulong
byte explicit lock sbyte unchecked
case extern long sealed unsafe
catch false namespace short ushort
char finally new sizeof using
checked fixed null stackalloc virtual
class float object static void
const for operator string volatile
continue foreach out struct while
decimal goto override switch
default if params this
delegate implicit private throw
#
//
// THIS PROGRAM CANNOT BE COMPILED
public class Esimerkki
{
    public static void Main()
    {
        int event;
        event = 52;
        System.Console.WriteLine(event);
    }
}

 

#
Which definitions are right?

Which of the following variable definition are both syntactically right and abide by coding conventions?

#

7.5 The scope of variables

The scope of variables refers to the usability of variables in different situations. If the variable is “in scope”, the variable can be used in this particular point in the code.

A variable can only be used (read and assigned values) in the block where it has been defined. A block starts with the brace { and ends with the brace }.

 {
    int number = 5; 
 }

Variables exist for as long as the block is not exited. During a subroutine call the block has not been exited, because the execution returns to the block after the subroutine has been executed. A nested block does not cause exiting either.

 {
    int number = 5; 
    SubroutineCall(); 
    {
        number++; 
    }
 }

The definition of a variable must always precede (be earlier in the code) its first use. Even within the same block the variable needs to be defined before its use, because the variable turns visible only after it has been defined.

#
//
    {
        number++;  // DOES NOT WORK, the variable hasn't been defined yet
        {
          number++; // DOES NOT WORK, the variable hasn't been defined yet

          int number = 5;

          number++; // WORKS
          System.Console.WriteLine(number);
        }
        number++; // DOES NOT WORK, not in scope
    }

 

In the following example, number and d are in scope and transformable only in the main program (except if taken as an out-parameter in C#). Every defined variable in the main program (or in any other subroutine) live until the closing brace } of the main program. Here the value of the variable is copied into a corresponding parameter in a subroutine. The subroutine cannot “see” the variable in the main program in any way, rather the subroutine only receives the value from the information that has been relayed to it.

The variable defined in the subroutine cannot be viewed by other subroutines; this is called a local variable. Variables number and d are local variables in the Main program.

#
//
    public static void Main()
    {
        int number = 9;
        double d = 0.5;
        Change(2, number);
        System.Console.WriteLine("number = {0}, d = {1}",number,d);
    }

 

In the example the name of the parameter variable is the same number as in the main program, but the name could be anything else. The most important point is that in the subroutine call, the variable in the corresponding place in the subroutine is assigned the same value as in the program that calls it. Even if the variable number was assigned some value, it won’t affect the program that calls the subroutine, because number is its own local variable in the subroutine and only exists until the execution of the subroutine reaches the closing brace } of the subroutine.

#
//
    public static void Change(int age, int number)
    {
        age--;
        int newValue;
        newValue = number +3;
        number = 12;
    }

 

When compiling, the program warns that the subroutine variable newValue is not used after it has been assigned a value. If the subroutine was called again, a new newValue variable would be created with the call, and it wouldn’t have anything to do with the corresponding value in the previous call.

The auxiliary variable newValue is in scope after it has been defined in the subroutine, but it stops existing after the closing brace }. Changes made to this variable (even if there is a variable of the same name somewhere) in no way affect any other place than this variable. The main program or anything else in the program cannot reach this variable in any way (except in this case this value depends on the number variable given as a parameter)

Changing parameter variable is not usually considered as abiding good conventions. If the parameter variables need to be changed, it’s better to make a local copy of it and change the local copy so that by the end of the subroutine, the parameter still has the same value as it did at the beginning of the subroutine.

Below, we have defined subroutines within a class. All variables are local variables.

#
public class LocalVariables
{
    public static void Main()
    {
        int number = 9;
        double d = 0.5;
        Change(2, number);
        System.Console.WriteLine("number = {0}, d = {1}",number,d);
    }

    public static void Change(int age, int number)
    {
        age--;
        int newValue;
        newValue = number +3;
        number = 12;
    }
}

 

A variable can also be defined to be in scope everywhere within a class, so for all subroutines. When a variable is in scope to all parts in a program, it is called a global variable. Global variables should be avoided whenever possible.

#
public class GlobalVariables
{
    public static int points;
    public static int result;

    public static void Main()
    {
        result = 10;
        System.Console.WriteLine("points = {0}, result = {1}",points,result);
        Change();
        System.Console.WriteLine("points = {0}, result = {1}",points,result);
    }

    public static void Change()
    {
        result += 10;
        points = 15;
    }
}

 

The variables points and result above are global variables, because they are defined outside of subroutines. They are also in use from classes outside this class, because they are unfortunately defined with the access modifier public. If the word static was missing from the definition of the variable, they could not be used in static subroutines. In this case, the variables would be attributes and to use them we would have to create an object which would contain and define the attributes. This is extra information that is not a part of the course’s main content.

#
//
   /// <summary>
   /// Studies the scope of variables
   /// </summary>
   public class ScopeOfVariables
   {
       /// <summary>
       /// The variables of the main program are in scope
       /// </summary>
       /// <param name="args">not in use</param>
       public static void Main(string[] args)  // args is in scope in the main program
       {
           int number = 9;   // In scope only in the main program
           double d = 5.5;  // In scope only in the main program
           System.Console.WriteLine("Before the change: {0}, {1}", number, d);
           Change(2, number);
           {                          // auxiliary block with its own variables
               int new = 3;          // variable which is in scope only in this block
               System.Console.WriteLine("new: " + new);
           }                          // now the variable new stops existing
           // now the variable new doesn't exist
           System.Console.WriteLine("After the change: {0}, {1}", number, d);

       }
       /// <summary>
       /// Tries to change the local variables of the main program in a subroutine
       /// </summary>
       /// <param name="newValue"> the new value assigned to the variable
       //      visible only in the subroutine, changing does not affect any program calling the subroutine</param>
       /// <param name="number">a variable the value of which is changed,
       //      visible only in the subroutine, the same name doesn't matter,
       //      changing does not affect any program calling the subroutine</param>
       public static void Change(int newValue, int number)
       {
           newValue--;             // does not affect the main program
           int newValue;         // a local variable in the subroutine
           newValue = number + 3;
           number = 12;            // does not affect the main program
       }

   }

 

Next, try what happens if you write the assignment d=4 in the subroutine Change.

#

The same variable name can be used again in different scope. However, it is a different variable in each scope area.

public class SameName
{
    public static void Main()
    {
        int i = 4;
        System.Console.WriteLine("The i in the main program = {0}",i);
        Sub1(i);
        System.Console.WriteLine("The i in the main program = {0}",i);
        Sub2();
        System.Console.WriteLine("The i in the main program = {0}",i);
    }


    public static void Sub1(int i)
    {
        System.Console.WriteLine("i in Sub1 = {0}",i);
        i++;
        System.Console.WriteLine("i in Sub1 = {0}",i);
    }

    public static void Sub2()
    {
        int i = 8;
        System.Console.WriteLine("i in Sub2 = {0}",i);
        i++;
        System.Console.WriteLine("i in Sub2 = {0}",i);
    }
}

 

However, C# does not allow a nested block use the same variable name that has been used in the outer block. However, if a global and a local variable have the same name, the local variable is in scope in its own block.

Try to comment the line i=9 away so that the program is compiled and prints the local i in the main program. If the line i=5 is commented away too, the global variable i is printed.

#
public class SameInNestedBlock
{
    public static int i = 6;

    public static void Main()
    {
        int i = 5; // replaces the global variable locally
        System.Console.WriteLine("The i in the outer block = {0}",i);
        {
            int i = 9; // THIS WON'T BE COMPILED
            System.Console.WriteLine("The i in the nested block = {0}",i);
        }
    }
}

 

More information (currently only in Finnish) about the scope of variables can be found on the extra material page of the course.

7.6 Constants

One man’s constant is another man’s variable. -Alan Perlis

In addition to variables, constants can also be defined in programmming languages. The values of constants cannot be changed after definition. In C# a constant is defined like any other variable, but the additional modifier const is provided before the type of the variable.

#
//
        const int NUMBER_OF_MONTHS = 12;
        // NUMBER_OF_MONTHS = 13; // try to remove the //

 

In this course, constants are uppercase and words within the name are separated with an underscore (_). This way they are easily distinguishable from variable names that are not capitalised. Other ways to write constants exist, for example Pascal Casing is the second most popular convention for writing constants.

#

Task 7.6

The program defines 10 variables and one constant in total. After each one, add a comment where you tell if it's a variable or a constant and its type (global, local, parameter)

//
public class Example
{

    static int number1 = 1;
    static int number2 = 2;

    public static void Main()
    {
        {
             const int NUMBER3  = 3;
             int number4 = 4;
        }
        //FROM HERE

        int number5 = 5;

        //TO HERE
        {
            int number3 = 3;
            int number4 = 4;
        }
    }

    public static void Subroutine(int number5, int number6)
    {
        int number7 = number5;
        int number8 = number6;
    }
}

 

#
Check your understanding

The previous task contains the comments `//FROM HERE` and `//TO HERE`. Which variables are in scope in the area between these two comments?

#
Check your understanding

Whic of the following statements are correct?

7.7 Operators

We often need to save the results of different calculations into variables. In C# calculations can be done with arithmetic operations, which were already mentioned in the context of the snowman example. The arithmetic calculations in programs are called arithmetic expressions.

C# also contains comparison operators, logical operators, bitwise operators, shortcut operators, the assignment operator =, the is operator, and the condition operator ?. The most important operators will be discussed in this chapter.

7.7.1 Arithmetic operations

Basic calculations in C# are performed with arithmetic operaions, of which + and - were already handled in examples before. There are five arithmetic operators.

Table 3: Arithmetic operations.

Operator Function Example
+ adding Console.WriteLine(1+2); // 3
- subtraction Console.WriteLine(1-2); // -1
* multiplication Console.WriteLine(2\*3); // 6
/ division Console.WriteLine(6 / 2); // 3
\ \ Console.WriteLine(7 / 2); //Huom! 3
\ \ Console.WriteLine(7 / 2.0); // 3.5
\ \ Console.WriteLine(7.0 / 2); // 3.5
% remainder (modulo) Console.WriteLine(18 % 7); // 4

Note: 18/7 = 2 4/7. The integer division / returns 2 and the remainder returns 4. Remainders are often used to test if a number is divisible by some number, for example:

#

Animation: Perform arithmetic operations

Step through the loop by clicking the green arrow. Study the function of operations
#
//
        int year = 2001;
        if ( year % 4 != 0 )
           System.Console.WriteLine("the year is not a leap year");

 

#

Task 7.7

Try replacing the + sign with all of the aforementioned operators. Before running the program, think about what the program will print and write your 'guess' in the task below. After running the program write what the program actually printed.

//
        int number1 = 17;
        int number2 = 2;
        int result = number1 + number2;

 

#

Write what each operator prints

+   result = 19
-   result =
*   result =
/   result =
%   result =

 

7.7.2 Comparison operators

Comparison operators compare the values of variables with each other. Comparison operators return a truth value (true or false). There are six comparison operators. More comparison operators are presented in chapter 13.

#

7.7.3 Shortcut operators

Shortcut operators allow presenting calculations in a more concise format: for example ++x; (four characters) means the same as x = x+1; (six characters). They can also be used to initialize variables.

Table 4: Shortcut operators.

Operator Function Example
++
Increment operator. Increments the
value of the variable by one.
int number = 0;\
Console.WriteLine(number++); // 0
Console.WriteLine(number++); // 1
Console.WriteLine(number); // 2
Console.WriteLine(++number); // 3
-- Subtraction operator.
Reduces the value of the variable by one.

int number = 5;

Console.WriteLine(number--); // 5
Console.WriteLine(number--); // 4
Console.WriteLine(number); // 3
Console.WriteLine(--number); // 2
Console.WriteLine(number); // 2

+=
Increment operation
int number = 0;\
number += 2; // the value of the number variable is 2
number += 3; // the value of the number variable is 5
number += -1; // the value of the number variable is 4
-=
Subtraction operatio
n| int number = 0;
number -= 2; // the value of the number variable is -2
number -= 1; // the value of the number variable is -3
*=
Multiplication operation
int number = 1; number *= 3; // the value of the number variable is 3 number *= 2; // the value of the number variable is 6\
/=
Division operation
double number = 27; number /= 3; // the value of the number variable is 9 number /= 2.0; // the value of the number variable is 4.5\
%=
Remainder operation
int number = 9;\
number %= 5; // the value of the number variable is 4
number %= 2; // the value of the number variable is 0

The increment operator (++) and the subtraction operator (--) can be used before of after the variable. When used in front of the variable, the value is changed first and the possible action, for example printing, is performed afterwards. If the variable is followed by the operator, the function is performed first and the value is changed afterwards.

Note! Value changing operators are so-called side-effect operators. In other words, the operations change the value of the variable, unlike arithmetic operations for example. The next example illustrates this.

#
//
        int number1 = 5;
        int number2 = 5;
        System.Console.WriteLine(++number1); // prints 6;
        System.Console.WriteLine(number1++); // prints 6;
        System.Console.WriteLine(number1); // prints 7
        System.Console.WriteLine(number2 + 1 ); // prints 6;
        System.Console.WriteLine(number2); // 5

 

7.7.4 The order of arithmetic operations

The order of arithmetic operations is equivalent to the order of operations in mathematics. Multiplication and division is always performed before addition and subtraction. Also, the expressions in parantheses are performed first.

#
//
        System.Console.WriteLine(5 + 3 * 4 - 2);  //prints 15
        System.Console.WriteLine((5 + 3) * (4 - 2));  //prints 16

 

7.8 Remarks

7.8.1 Assigning integers to floating point number variables

When we attempt to save the result of a division of integers into a floating point number type (float or double) variable, the result may be saved as an integer if the divisor and the dividend are both entered without the decimal part.

#
//
        double result = 5 / 2;
        System.Console.WriteLine(result); // prints 2

 

However, if even one of the numbers in the division provided in decimal format, the result of the operation is saved into the variable correctly.

#
//
        double result = 5 / 2.0;
        System.Console.WriteLine(result); // prints 2.5

 

When making calculations with floating point numbers, it is best to keep all numbers in decimal format, even the integers, for example, by giving the number 5 in format 5.0.

When making calculations with integers, note the following:

#
//
        int result = 5 / 4;
        System.Console.WriteLine(result); // prints 1

        result = 5 / 6;
        System.Console.WriteLine(result); // prints 0

        result = 7 / 3;
        System.Console.WriteLine(result); // prints 2

 

As we can see above, integers are not rounded to the nearest whole number, but rather the decimal part in division is “lost” in C#. If both the divisor and the dividend are integer variables, the division is cut into an integer. This problem can be avoided by starting off with a real number term.

#
//
        int number1 = 5;
        int number2 = 2;
        double result = number1 / number2;
        System.Console.WriteLine(result); // prints 2
        result = 1.0 * number1 / number2;
        System.Console.WriteLine(result); // prints 2.5

 

#

Tehtävä 7.7.1 What effect do parentheses have?

What happens in the example above if we put 'number1/number2' in parentheses?

 

7.8.1.1 Task 7.8

See all answer options below. First, think about the answer for each question and only then view the lecture video to see the right answer.

Number 1 2 3 4 5 6 7 8 9
Answer 0 1 1.5 2 7 8 9 13 The program crashes
7.8.1.1.1 What is the result of the following expressions?
7.8.1.1.2 What are the values of the variables?
#
int a = 5 + 10 % 6 / 3 + 1; value of a afterwards? Answer – 1h5m50s (49s)
#
double d = 5 + 10 % 6 / 3 + 1; value of d afterwards? Answer – 1h7m10s (10s)
#
double e = 5.0 + 10 % 6 / 3 + 1; value of e afterwards? Answer – 1h7m56s (1m49s)
#
double e = 5.0 + 10.0 % 6 / 3 + 1; value of e afterwards? Answer – 1h10m20s (39s)

7.8.2 About addition and subtraction operators

There are four ways to increment the value of a number by one.

#
//
        int a = 3;
        int b,c;
        b = ++a; // Note! This would not have to be assigned anywhere!
        ++a;     // this is a way to increment
        c = a++; // an idiom. c receives the original value of a and then a is incremented.
        a++;  // this can also be used (and is often used) without assignmnent
        a += 1;
        a = a + 1; // the worst option in terms of alternating and writing.

 

In programming, an idiom refers to the way in which something should be done. From the example above, a++ is an established practice in programming and the most recommended option, i.e. an idiom. However, if the variable a should be incremented (or subtracted) by two or three, this practice would not work. The next example illustrates different ways to subtract by two. There are three alternatives for this.

#
//
        int a = 10;
        a -= 2;
        a += -2;  // The addend can also be an expression. The number can also be negative
        a = a - 2;

 

In this example, the use of the += operator is the most recommended option, because the number to add can be positive or negative (or zero), so the += operator does not limit what kinds of numbers can be added to the variable a.

#

Animation: Perform operations

Step by clicking the green arrow. Note that it is a bad convention to assign result = result++; like in this example: this is never really done. Study the operators.

7.8.3 Be careful not to divide by zero

One of the most common programming mistakes is dividing by zero. This is not a syntax error, because it can seldom be noticed during compilation. In other words, dividing by zero is a logical error that goes unnoticed until during runtime. The programmer needs to make sure that the divisor is never zero. However, the conditional statement (if), which will be introduced later, is needed for aid here:

#

See what happens when you try to run the program.

//
        double result;
        int divisor = 3;
        int dividend = 7;
        result = dividend / (divisor - 3);

 

7.9 Example: Body Mass Index

Let’s make a program that calculates a person’s body mass index (BMI). BMI is calculated by dividing the weight (kg) by the height (m) squared, i.e. with the equation:

weight / (height * height)

in C#, the BMI can be calculated as follows.

#
//
/// @author  Antti-Jussi Lakanen
/// @version 22.8.2012
///
/// <summary>
/// A program that calculates a person's BMI based on
/// the weight (kg) and the height (m).
/// </summary>
public class MassIndex
{
    /// <summary>
    /// The main program that prints the BMI on screen.
    /// </summary>
    public static void Main()
    {
      double height = 1.83;
      double weight = 75.0;
      double bmi = weight / (height*height);
      System.Console.WriteLine("Your BMI is {0:0.00}",bmi);
   }
}

 

#

Task 7.9

Change the previous example so that the BMI is calculated in a subroutine that is called from the main program. You can also make the subroutine print the result as well, but then give it a descriptive name, for example PrintBMI. Add documentation comments.

//

 

#

Task 7.10

Study the Jypeli class list. Find the Level class and list its attributes here. Give the return value of the attribute as well.

 

#

Task 7.11

Earlier, we made a subroutine that prints the text "Hello World". Now make a subroutine that prints the text that is provided as a parameter. Add documentation comments as well.

//
using System;

public class Print
{
    public static void Main()
    {
        String text = "Jeps Jeps";
        PrintText();

    }


    public static void PrintText() {


    }
}

 

#

8. Object data types

The primitive data types in C# provide a very limited framework for programming. They can only store numbers (int, double, etc.), single characters (char), and truth values (bool). Even slightly more complex programs require more advanced structures for storing information. In C#, Java, and other object-oriented languages these structures are provided by objects. In C#, even a string of characters (string) is an object.

8.1 What are objects?

An object is a data structure that is aimed to represent a real world phenomenon in programming. In class-based languages (such a C#, Java, and C++), the structure and behaviour of an object is defined by its class, which describes the attributes and methods of the objects created from it. Objects have different attributes, and methods describe the functions of an object. An object is said to be an instance of the class. So, multiple objects with the same attributes and functions can (usually) be created from one class. Attribute values form the state of the object. Note however that even if several objects have the same state, they might still have a different identity. For example, two exactly identical circles can appear in the same position (=look like one circle), but in reality there are two different circles.

Objects can be created on your own, or you can use ready-made objects in libraries. Making your own object classes is not required on the Programming 1 course, but using object classes is. Next, we will study the relationship between a class and an object, and how to use an object.

The relationship between a class and an object can be described with the following example. Let’s say that there are several people in a lecture hall. Everyone in the lecture hall is a person. They have certain features that all people have, for example a head, a nose, and other body parts. However, everyone in the lecture hall is a different instance of a person, so each object has their own identity - they are not one and the same, there are several of them. Different people can have different hair and different colour eyes and their own manner of speech. Additionally, people can be of different height, weight, etc. Even identical twins in the lecture hall would each be a different instance of a person, i.e. a Person object. Hair, eyes, height, weight would be object attributes. A Person might also have functions, i.e. methods, for example Eat, GoToWork, Study, etc. Next, we will study a more conventional example of objects.

Let’s assume that we are designing a pay system for a company. We would need an Employee class, among others. The Employee class would need at least the following attributes: name, job, department, salary. The class would have at least the following methods: PaySalary, ChangeJob, ChangeDepartment, ChangeSalary. Each employee would be their own instance of the Employee class i.e., an object.

8.2 Creating objects

Employee joe = new Employee("Joe Bungler", "Project manager", "Research department", 5000);

Object references are defined by first writing the name of the class from which the object is created. Next, we name the object (“joe” in the example above). After the name, we write the equals sign, after which we write the word new to inform that we are creating a new object. This new operator reserves space for the object from the memory of the computer.

Next, we write the name of the class again, after which we write (in parentheses) the parameters that are possibly required to create the object. Parameters depend on how the class constructor has been implemented. The constructor is a method that is always executed when a new object is created. However, in order to use ready-made classes, you don’t have to know about the implementation of the constructor, because the requires parameters can be read from the class documentation. In general format, a new object can be created in the manner below.

Class objectName = new Class(parameter1, parameter2,..., parameterN);

If an object doesn’t require any parameters for creating it, an empty pair of parentheses is written.

Before an object has been reserved a space from the memory of the computer by using the new operator, it cannot be used. Before the new operator the value of the object variable (i.e. the reference value) is null. Using an object with a null reference causes a run-time error. In some special cases, the value of an object variable can also intentionally be set as null by stating nameOfObject = null.

The value of an object cannot be null, but the value of the variable, which contains the reference to the object or the object itself, can be null.

07 Oct 17

A new Employee object could be created for example in the following way. The parameters depend on how the constructor of the Employee class has been implemented. In this case, we provide all the attributes as parameters to the object.

Employee donaldDuck = new Employee("Donald Duck", "Manager", "Department3", 3000);

At the beginning of this material, we drew snowmen by creating objects of the PhysicsObject class as follows.

PhysicsObject p1 = new PhysicsObject(2 * 100.0, 2 * 100.0, Shape.Circle);

In fact, an object variable in C# is only a reference to an object. This is why they are often called reference variables as well. Reference variables differ significantly from primitive data type variables.

8.3 The difference between object data types and primitive data types

C# contains two types of structures that can store information. Depending on the case, information is either stored into primitive data types or object data types. Object data types differ from primitive data types in that they are references to a certain object and, for this reason, they are also called reference types or reference variables.

  • Primitive data types store their information in one place in the computer memory (a stack)

  • Reference types contain a reference to another place in memory (a heap), where the data itself is located. However, the reference to the object remains in the stack.

Usually we won’t have to worry about whether we use primitive data types (like int, double, or char) or object data types (like string). Generally, the most important difference is that primitive data types should (apart from a few exceptions) always have a value, but object data types can also have a null value (i.e. “nothing” value). Later, some examples of the differences between primitive data types and reference data types will be presented.

Several variables can refer to the same object. Compare the lines of code below.

#
//
        int number1 = 10;
        int number2 = number1;
        number1 = 0;
        System.Console.WriteLine(number2); //prints 10

 

The code above prints “10” as it should. The value of variable number2 won’t change even when we assign the value 0 to number1 on line three. The reason for this is that on the second line, we assign the value of number1 to the variable number2, and not the reference to variable number1. Object data type variables act differently. Compare the example above to the following example:

#
        PhysicsObject p1 = new PhysicsObject(2*100.0, 2*100.0, Shape.Circle);
        Add(p1);
        p1.X = -200;

        PhysicsObject p2 = p1;
        p2.X = 100;

 

The code above draws the following image:

#

Image 8: Both variables, p1 and p2, move the same circle. The result is a circle in the position x=100.

We could make the rash assumption that the image just shows two similar circles in the same location. This is not the case, however: both of the PhysicsObjects refer to the same circle with radius 50. This is caused by the fact that variables p1 and p2 are object references which refer (or point) to the same object.

PhysicsObject p2 = p1;

In other words, the line above does not create a new PhysicsObject, only a new object reference which now refers to the same object as p1.

#

Image 9: Both p1 and p2 refer to the same object.

Object variable = reference to an object. One object can have several references to it.

References are discussed in detail in chapter 14.

8.4 Calling methods

Each object created from a certain class has all the public methods of the class in its use. In the method call we command the object to do something. For example, we could command the PhysicsObject to move, or the Employee object to change its salary.

Object methods are called by writing the object’s name, a dot (.), and the name of the method to call. The possible method parameters are placed within parentheses and divided by commas. If the method doesn’t require any parameters, the parentheses are still required, they simply won’t contain anything. The method call in general format:

nameOfObject.MethodName(parameter1,parameter2,...parameterN);

For example, we could change the salary of the donaldDuck object with the following.

donaldDuck.ChangeSalary(3500);

Or make the p1 object (assuming that p1 is a PhysicsObject) move with the Hit method.

p1.Hit(new Vector(1000.0, 500.0));

String class has for example the Contains method that returns either the value True or False. The Contains method is given a string as a parameter, and the method will search for instances of the provided string in the object. If the object contains the string (once or several times), it returns True. Otherwise it returns False. An example of this below.

#
//
        string sentence = "Pekka went to the grocery store";
        Console.WriteLine(sentence.Contains("ent")); // Prints True

 

8.5 The difference between methods and subroutines

A subroutine is introduced as static if the subroutine does not use any other information apart from the information provided as parameters. For example, section 20.4.2 contains the following subroutine.

private void ListenToMovement(AnalogState mouseState)
{
   ball.X = Mouse.PositionOnWorld.X;
   ball.Y = Mouse.PositionOnWorld.Y;

   Vector mouseMovement = mouseState.MouseMovement;
}

Here, in addition to the mouse state, the information of the ball object introduced in the game object (this) is required, so this is no longer a static subroutine, which is why the word static was left out. However, a method can use the object’s own attributes, methods, and the so-called property fields. Remember that the object’s own properties can be referred to like this as well:

   this.ball.X = Mouse.PositionOnWorld.X;

so if the subroutine needs the this reference, it is a method (not static).

8.6 Destroying objects and garbage collection

When there are no variables (object references) that refer to an object anymore, the memory locations must be freed for other use. The objects are removed from memory with the help of a cleaning operation. In C#, garbage collection takes care of this. When there are no longer any references to an object, it is marked as removable, and every now and then the garbage collector frees the memory locations of the marked objects.

All programming languages don’t have this feature (e.g. original C++), in which case freeing up memory and destroying objects needs to be taken care of manually. These languages usually have a destructor which is executed whenever an object is destroyed. Self-made destructors usually call the destroying of the objects created during the object’s life cycle and freeing of other resources. Compare to the constructor which is executed when the object is created. The challenge in these languages is that sometimes the life cycle of objects is automatic in some cases and sometimes not. This can easily cause a memory leak, i.e. a memory location is not freed up, but it no longer has any pointers to it that would enable using it, which will leave the memory location reserved for the remainder of the program. Memory leaks are very common for starting C++ programmers. Languages like Java and C# have made it remarkably easy to avoid memory leaks.

Usually C# programmer don’t have to worry about freeing up memory, but there are certain situations in which you may have to remove objects by yourself. One example of this is handling files: if an object has opened a file, it would be sensible to close the file before destroying the object. In this case, closing the file would have to be performed in the same context as destroying the object. This could be done by introducing a destructor which is a class method that strips the object of all the information it contains and frees up all the constructions it contains, like links to open resources (e.g. files; however, it is not recommended to keep files open for times as long as the duration of the entire life cycle of an object)

8.7 Documentation of object classes

Class documentation contains information about the class, the constructors of the class, and methods. Class documentation usually contains links to examples, like in the case of the String class. Now, we will study the documentation of the String class in more detail. The documentation of the String class can be found here: http://msdn.microsoft.com/en-us/library/system.string.aspx. The documentation contains e.g. a list of members, i.e. the constructors, attributes (fields), properties, and methods that can be used.

At this point, we are interested in the String Constructor and String methods (in the hierarchy tree on the left side of the page). Click String Constructor to get more information about the class constructors and click String Methods to get more information about the methods that can be used.

8.7.1 Constructors

Open the String Constructor page of the String class. This page contains all the information about the class constructors. There can be several constructors as long as their parameters differ in some way. Each constructor has its own page, and each programming language has its own version (the .NET Framework contains several programming languages). Naturally, we are now interested only in the C# versions.

For each constructor, there is a short description of what it does, which types of and how many parameters it receives. You can click the introductory line of the constructor to see more information about it. For example the link

takes you to the page (http://msdn.microsoft.com/en-us/library/ttyxaek9.aspx) which contains more information and usage examples of the constructor public String(char[]).

#

Image 10: Information about the class constructors can be found in the Constructor section of the MSDN Documentation.

Note that many of the constructors in the String class have been marked as unsafe, which means that they should not be used in your code. These types of constructors are only used for inter-system communication.

At this point it might be difficult to understand the meaning of all the constructors, because they contain data types that we haven’t discussed yet. For example, the square brackets after the data type (e.g. int[]) mean that the data type is an array (a table). Arrays will be discussed in chapter 15.

A String object is perhaps the most common object in C#, and it is in fact a collection (an array) of consecutive char type characters. It can be created as follows.

#
//
        string name = new String(new char [] {'J', 'a', 'n', 'n', 'e'});
        Console.WriteLine(name); // Prints Janne

 

Of course, this way of writing strings is burdensome. However, a String object can exceptionally be created in a manner that resembles the definition of primitive data types. The statement below is equivalent to the example above, but it is much shorter.

#