Dr. Martin Chapman programming@kcl.ac.uk martinchapman.co.uk/teaching Programming Practice and Applications (4CCS1PPA) Topic 7: Arrays Q: What is the non-technical definition of an array? 1 Thursday 17th November
To understand how to employ the array data structure in our programs. • To build a working representation of the game of Noughts and Crosses. ARRAYS: OBJECTIVES 2
Given this set of numbers — 32, 54, 68 and 30 — write a program that stores these numbers, and then examines each of them and prints the word YES if the number is greater than the average (46). EXERCISE: HANDLING SETS OF NUMBERS (1) 3
EXERCISE: HANDLING SETS OF NUMBERS (2) 4 int numberOne, numberTwo, numberThree, numberFour; numberOne = 32; if ( numberOne > 46 ) { System.out.println("YES"); } numberTwo = 54; if ( numberTwo > 46 ) System.out.println("YES"); numberThree = 68; if ( numberThree > 46 ) System.out.println("YES"); numberFour = 30; if ( numberFour > 46 ) System.out.println("YES"); 32 54 68 30
Ideally, rather than having to interact with each stored integer individually, we want to interact with a single set of connected integers. This necessitates a single label for all the stored values. EXERCISE: HANDLING SETS OF NUMBERS (3) 5 32 54 68 3030 68 54 32
An array is the answer. • Dictionary definition: ‘To arrange a group of things in a particular order’. An array is a type of variable with multiple slots. Each slot can hold a different value. The values in an array must all be of the same type. ARRAYS (1) 6 30 68 54 32
`A array is a type of variable’ ARRAYS (2) 7 int numberOne, numberTwo, numberThree, numberFour; `with multiple slots.’ int[] numbers int[] numbers = new int[4]; `My numbers variable has 4 slots’ `Each slot can hold a different value.’ numberOne = 32; numberTwo = 54; numberThree = 68; numberFour = 30; numbers[0] = 32; numbers[1] = 54; numbers[2] = 68; numbers[3] = 30; We place square brackets after a type to indicate that we want to store a sequence of values of this type.
Create an array… ARRAYS (3): CREATE AND STORE (SUMMARY) 8 …and then store things in that array. int[] numbers = new int[4]; numbers[0] = 32; numbers[1] = 54; numbers[2] = 68; numbers[3] = 30; 32 54 67 2930 68 54 32 [0] [1] [2] [3] The length of the array differs from the last index.
Because of this breakdown, we can imagine each character in a string as having an index. • This means that there is a distinct difference between the length of a string and the last index. REMEMBER: INDEX VS. LENGTH 9 ‘M’,‘a’,‘r’,‘t’,‘i’,‘n’ Character 5 Length = 6. We can leverage this idea in order to approach our current problem.
We could combine the steps we saw previously in order to declare and initialise an array in a single line. • A similar principle as a single line declaration and initialisation for a normal variable. • Thus, we would need to know the values we want in that array immediately. This would give us an array of a size equal to the number of values. ARRAYS (3): CREATE AND STORE (SINGLE LINE) 10 int[] numbers = { 32, 54, 68, 30 };
FIXED ARRAY SIZE 11 int[] numbers = new int[4]; numbers[0] = 32; numbers[1] = 54; numbers[2] = 68; numbers[3] = 30; When we specify the initial size of an array, this size is fixed, such that if we try and write to a non-existent index, the following occurs: numbers[4] = 52;
😴 Note that this error doesn’t occur at compile time (after the compiler is run), but instead at run time (after the virtual machine is run). • The compiler is (necessarily) too simplistic to catch this issue at compile time. • It also isn’t always possible to identify this type of error at compile time. For example, access to, or the size of, an array may occur as the result of user input. • We also saw an InputMismatchException runtime error in Topic 6. • We’ll return to these ideas. ERROR: INDEX OUT OF BOUNDS EXCEPTION 12
We first need to work out how to determine the length of an array. HOW DO WE AVOID A NON-EXISTENT INDEX IN AN ARRAY BEING WRITTEN TO? (1) 13 int[] numbers = new int[4]; System.out.println("The length of the " + "numbers array is: " + );numbers.length If we want to split a string over two lines, we need to concatenate.
HOW DO WE AVOID A NON-EXISTENT INDEX IN AN ARRAY BEING WRITTEN TO? (2) 14 int i = 4, value = 52; int[] numbers = new int[4]; if ( 0 <= i && i < ) { numbers[i] = value; } numbers.length We can then combine this information with a conditional statement, to ensure that a non-existent index in an array is not written to. This test would become particularly important if we weren’t (directly) in control of the value in i.
We’ve seen the word length used in two places now. • To determine the length of a string. ASIDE: ARRAY LENGTH VS. STRING LENGTH 15 • This is a method call (brackets). • To determine the length of an array. password.length() numbers.length • This resembles a reference to a field (no brackets). • More shortly.
In order to get data back from an array, we simply reference the index of the data that we want. HOW DO WE GET DATA BACK FROM AN ARRAY? 16 int[] numbers = new int[4]; numbers[0] = 32; numbers[1] = 54; numbers[2] = 68; numbers[3] = 30; System.out.println("The value in slot 2 is: " + numbers[2]);
Given this set of numbers — 32, 54, 68 and 30 — write a program that stores these numbers, and then examines each of them and prints the word YES if the number is greater than the average (46). Hint: Think about how we might extend the idea of using index and length, as seen in Slide 14. EXERCISE: BACK TO HANDLING SETS OF NUMBERS (1) 17
REMEMBER: LEVERAGING THE LOOP INDEX (3) 18 public class NumberPrinter { public void printNumber(int num) { System.out.println("+------+"); System.out.println("|" + num + "|"); System.out.println("+------+"); } } public class Driver { public static void main(String[] args) { NumberPrinter numberPrinter = new NumberPrinter(); for ( int i = 1; i < 4; i++ ) { numberPrinter.printNumber(i); } } } As the loop index is just a variable declaration, it can be referenced inside the loop.
EXERCISE: BACK TO HANDLING SETS OF NUMBERS (2) 19 int[] numbers = new int[4]; numbers[0] = 32; numbers[1] = 54; numbers[2] = 68; numbers[3] = 30; for ( int i = 0; i < numbers.length; i++ ) { if ( numbers[i] > 46 ) System.out.println("YES"); } We use our knowledge of loop bounds to carefully control how we access each index. We could shorten this to a single line initialisation and declaration.
Let’s imagine we want to extend our number checker to instead store an arbitrary amount of numbers from a user, and then check which are over the average. CAPTURING AN UNKNOWN AMOUNT OF USER INPUT (1) 20
REMEMBER: TRACKING A USER’S CALORIES (3) 21 import java.util.Scanner; public class CalorieTracker { public static void main(String[] args) { Scanner in = new Scanner(System.in); Person person = new Person(); System.out.println("Enter the calories in your starter:"); Dish starter = new Dish(); starter.setCalories(in.nextInt()) Every time we invoke the nextInt method, our program will stop and wait for another token of user input.
CAPTURING AN UNKNOWN AMOUNT OF USER INPUT (2) 22 import java.util.Scanner; Scanner in = new Scanner(System.in); in.nextInt(); in.close(); int[] numbers = new int[ ]; for ( int i = 0; i < ; i++ ) { numbers[i] = } What are our options here? ? ? Let’s fill in as much of the solution as we can: Familiar scanner syntax. Familiar array syntax.
OPTION 1: LET THE USER SPECIFY A MAXIMUM 23 import java.util.Scanner; Scanner in = new Scanner(System.in); in.nextInt(); in.close(); int[] numbers = new int[ ]; for ( int i = 0; i < ; i++ ) { numbers[i] = } What if the user themselves is unsure? in.nextInt() numbers.length
OPTION 2: SPECIFY A MAXIMUM FOR THE USER (1) 24 import java.util.Scanner; Scanner in = new Scanner(System.in); in.nextInt(); in.close(); int[] numbers = new int[ ]; for ( int i = 0; i < ; i++ ) { numbers[i] = } 100 numbers.length
OPTION 2: SPECIFY A MAXIMUM FOR THE USER (2) 25 import java.util.Scanner; Scanner in = new Scanner(System.in); in.nextInt(); in.close(); int[] numbers = new int[ ]; for ( int i = 0; i < ; i++ ) { numbers[i] = } 100 numbers.length What if someone wants to enter less than 100 numbers?
OPTION 2: SPECIFY A MAXIMUM FOR THE USER (3) 26 import java.util.Scanner; Scanner in = new Scanner(System.in); in.nextInt(); in.close(); int[] numbers = new int[ ]; while ( in.hasNextInt() ) { numbers[i] = } 100 What if someone wants to enter less than 100 numbers? How do we know where to place the next number? We could keep looping while there are still integers to read. System.out.println("Enter N numbers." + "Type 'done' to finish.");
This problem stems from the fact that arrays (initially) have to be a fixed size. Therefore, if a programmer wants to account for an unknown number of input values (such as input coming from a user), they have to specify a large fixed size (like we have done), and prepare for this array to be partially filled with values. Storing values in a partially filled array requires an extra step… PARTIALLY FILLED ARRAYS (1) 27
PARTIALLY FILLED ARRAYS (2) 28 Scanner in = new Scanner(System.in); int[] numbers = new int[100]; while ( in.hasNextInt() ) { numbers[ ] = in.nextInt(); } in.close(); import java.util.Scanner; int elements = 0; elements++ We define an additional variable to track the next free slot in the array. Every time we add an item to the array, we increment this variable.
PARTIALLY FILLED ARRAYS (3) 29 If our user intends to enter a large amount of numbers… …then eventually the elements variable may cause problems.
PARTIALLY FILLED ARRAYS (4) 30 Scanner in = new Scanner(System.in); int[] numbers = new int[100]; while ( in.hasNextInt() && ) { numbers[ ] = in.nextInt(); } in.close(); import java.util.Scanner; int elements = 0; elements++ elements < numbers.length We can also use our element index variable to check whether it’s possible to continue adding items to the array.
Once a user has finished inputting their numbers, we can examine what they have entered by again using our elements variable. ACCESSING DATA IN A PARTIALLY FILLED ARRAY (1) 31 for ( int i = 0; i < elements; i++) { total += numbers[i]; } for ( int i = 0; i < elements; i++) { if ( numbers[i] > total / elements ) System.out.println("YES"); } (double) Length is no longer useful because it refers to the length of the array, not the number of items in it.
There is an alternative here, something called the for each loop (originally mentioned in Topic 5): ACCESSING DATA IN A PARTIALLY FILLED ARRAY (2): FOR EACH (1) 32 for ( int number : numbers ) { total += number; } 30 68 54 32 Each number is automatically taken from the array and placed into a temporary variable, which can be referenced in the loop. Changes to this variable do not affect the array. The loop ends when all the items in the array have been examined.
I almost always use the for each loop. • A relatively new construct, hence the existence of the more traditional index approach. • Does limit flexibility slightly, given the lack of an index value. ACCESSING DATA IN A PARTIALLY FILLED ARRAY (3) 33 for ( int number : numbers ) { total += number; } for ( int number : numbers ) { if ( number > total / elements ) System.out.println("YES"); } (double)
If we were to fill partially fill an array with a certain number of values, but then accidentally access an index that was within the size of the array, but not manually filled, what would happen? • We would not be given an error, but instead a zero would be printed. • Arrays of a primitive type contain default values. ACCESSING DATA IN A PARTIALLY FILLED ARRAY (4) 34 int[] numbers = new int[100]; for ( int i = 0; i < 50; i++) { numbers[ i ] = i; } System.out.println(numbers[50]);
Recall that I also described objects as multiple slot variables in Topic 4, as I have with arrays, suggesting some connection between the two. • The fact that the slots in an integer array are given default values, also suggests some connection to the fields of an object. • As does the fact that the default values in an array, when an array is of a primitive type, are the same as those in the fields of an object, when those fields are of the same primitive type. Arrays are indeed objects (albeit a special type). ARRAYS VS. OBJECTS (1) 35
The array types themselves (e.g. int[], boolean[]) aren’t physical classes, but instead simulated runtime classes provided to us by the JVM, that have certain properties that are similar to those demonstrated by actual classes. • The use of the new command. • New copies to store different data. • Default field values (mentioned). • Public (immutable) fields. ARRAYS VS. OBJECTS (2) 36 numbers.length int[] numbers = new int[4];
Recall that in Topic 4 we determined that primitive types can be replaced with class types. This idea doesn’t stop with arrays. ARRAYS OF OBJECTS (1) 37 We can, as we might expect, connect a set of objects in an array. Indeed, we’ve had an example of this right under our noses for quite some time: int[] numbers = new int[4]; private Dish[] starterMainDessert = new Dish[3]; String[] args
We now know a even more about main. • We know that main can never return anything (where would it go anyway?) • This is a parameter, so main must accept some information. • But unfortunately, we still aren’t in a position to discuss what is passed to the main method. REMEMBER: BACK TO MAIN 38 public static void main(String[] args) { • We know that main has to be visible from outside the class, so that it can be run by the JVM (Topic 4). • We know that main can be accessed without having to make an object of our driver class, which makes things easier for the JVM.
We now know everything about main. • We know that main can never return anything (where would it go anyway?) • This is a String array, which is passed to the main method. • We know that main has to be visible from outside the class, so that it can be run by the JVM (Topic 4). • We know that main can be accessed without having to make an object of our driver class, which makes things easier for the JVM (Topic 6). REMEMBER: BACK TO MAIN 39 public static void main(String[] args) {
When we use software such as a video converter to convert files, or we use a word processor, we input our source files (.mp3 file, .docx file) into the software using the GUI. • We might right-click on the .docx file and select `open with Microsoft Word’. When we use software such as the Java compiler and the Java virtual machine to compile and run programs, we input our source files using a terminal (a command line interface). • It is possible to run compiled (Java) programs from the GUI, but it requires a more complex compilation configuration. REMEMBER: USING THE COMPILER AND THE JAVA VIRTUAL MACHINE 40
Each token we write after the name of our compiled program when we pass it to the Java virtual machine, will be passed, token-by-token, to each index of the args array. COMMAND LINE ARGUMENTS (1) 41 public class CommandLineArguments { public static void main(String[] args) { System.out.println(args.length); for ( String argument: args ) { System.out.println(argument); } } }
COMMAND LINE ARGUMENTS (2) 42 32 54 67 29 [0] [1] [2] [3] [4] Hello My Name Is Martin Each token we write after the name of our compiled program when we pass it to the Java virtual machine, will be passed, token-by-token, to each index of the args array.
A piece of data that we pass to a program via the command line is referred to as an argument. • Hence the abbreviation args (although this can be changed). • An argument, in the literal sense, is a piece of information from which another action may be inferred. • We also pass arguments (data) to parameters when we call methods. COMMAND LINE ARGUMENTS (3) 43
Command line arguments give us another way to take user input. As we can only do this once, this style of input only really applies to simple programs, or to setting flags that affect the overall operation of the program. To take input more than once, and in an arguably more user friendly manner (with the inclusion of print statements), we need to use the Scanner class, as we saw in Topic 6. SCANNER VS. ARGS (1) 44
😴 SCANNER VS. ARGS (2): SIMPLE PROGRAMS 45 public class Adder { public static void main(String[] args) { if ( args.length == 2 ) { System.out.println(Integer.parseInt(args[0]) + Integer.parseInt(args[1])); } } } Another Library class enabling the conversion of strings to integers. We will return to look at this class and this idea. Even if we enter numbers into the command line, each token is always treated as a string.
SCANNER VS. ARGS (3): SETTING FLAGS 46
REMEMBER: LECTURE EXERCISE: BLOCKCHAIN (4) - MINER AND BLOCKCHAIN 47 public class Miner { public static void main(String[] args) { Block myBlock = new Block(); Blockchain chain = new Blockchain(); chain.addBlock(myBlock); } } public class Blockchain { public void addBlock(Block block) { } } Keep a list of blocks Add the block to a list How do we do this? On to Topic 7! Back to arrays of objects…
REMEMBER: LECTURE EXERCISE: BLOCKCHAIN (4) - MINER AND BLOCKCHAIN 48 public class Miner { public static void main(String[] args) { Block myBlock = new Block(); Blockchain chain = new Blockchain(); chain.addBlock(myBlock); } } public class Blockchain { private Block[] chain; private int blocks; public Blockchain(int chainLength) { chain = new Block[chainLength]; } public void addBlock(Block block) { chain[blocks++] = block; } }
😴 Like accessing a variable of a class type when it is a field before it has had a copy of a class assigned to it, accessing an empty index in an array of a class type will result in a NullPointerException at runtime. • We’ve already seen InputMismatchExceptions and ArrayIndexOutOfBoundsExceptions. We will formalise these observations in Topic 10. NULL (1) 49 public class PairBalancer { private Pair toBalance; public PairBalancer(int valueA, int valueB) { toBalance.setValueA(valueA); toBalance.setValueB(valueB); }
This error may seem to contradict the idea that fields (and the indices in an array) are given default values. But in reality, there is a default value here, added in automatically for us: NULL (2) 50 public class PairBalancer { private Pair toBalance; public PairBalancer(int valueA, int valueB) { System.out.println(toBalance); }
😴 The value null can be seen as a special literal, placed inside variables of a class type before they are assigned a copy of a class, and thus become objects (under our current understanding). • Again, the true role of null will become clearer when we look at objects in memory. We can use this literal to protect against errors: NULL (3) 51 if ( toBalance != null ) { toBalance.setValueA(valueA); toBalance.setValueB(valueB); }
In the Java library, you’ll find a class called Arrays. ASIDE: THE ARRAYS UTILITY CLASS 52 The class has a number of useful static methods: int[] hulk = new int[10]; int elements = 0; for ( int i = 0; i < 100; i++ ) { if ( elements == hulk.length ) { hulk = Arrays.copyOf(hulk, hulk.length * 2); } hulk[elements++] = i; } System.out.println(Arrays.toString(hulk)); Double the size of the array as necessary (not particularly neat). Print the content of the array. Open question: what happens if we print the array directly? import java.util.Arrays;
Given the array of numbers we had earlier: LECTURE EXERCISE: LARGEST NUMBER (1) 53 Write a program that identifies the largest number in the list. int[] numbers = { 32, 54, 68, 30 };
LECTURE EXERCISE: LARGEST NUMBER (2) 54 int largest = numbers[0]; for ( int i = 1; i < numbers.length; i++ ) { if ( numbers[i] > largest ) { largest = numbers[i]; } } System.out.println("Largest: " + largest);
🖥 Some exercises involving arrays, for you to try in the laboratories: • Write a method that sums all the values in an array. • Fill an array, of size 50, with random numbers (between 1 and 100). Print out the contents of this array, 10 numbers per line, separating each number with a comma. Finish the whole sequence will a full stop. • With the same array, replace the number in the second index with the number `1337’, and shift every other number up by one place. ARRAY EXERCISES 55Topic7-1
Noughts And Crosses 56
😴 We’ve already discussed, and seen lots of examples of, how code can be used to model real world phenomena (i.e. a set of objects). Before we model something, we have to make a decision about the classes we want to include in our model. • Typically these are the nouns pertinent to the problem. • We will look more formally at this process next semester. We then build these objects individually, before combining them to complete our model. Let’s look at this process for the game of Noughts and Crosses. IDENTIFYING CLASSES 57
For the game of noughts and crosses, I would propose that we look at the following classes: CLASSES IN NOUGHTS AND CROSSES 58And a Main class. Game (or Board) Piece Move
Following requirements, or the design decisions made by someone else, is something you will now be familiar with from the assignment briefs. We have a solution in mind, and are using these requirements to guide you towards it. • Only way to structure the collective production of large programs (be this for an assignment, or as part of a lecture). • Some connection with industry, where you will be building to a specification. • Asks you to suspend the why slightly. FOLLOWING REQUIREMENTS (1) 59
When we follow requirements during a lecture, to produce a solution together, we will do so with the aid of offline versions. You will find these on KEATS. FOLLOWING REQUIREMENTS (2): VERSIONS 60 /** * A class that represents A, B and C. * * Version 1. * * New in this version: * * 1. field D * 2. method E * * @author Martin * */ public class F { • Show you incremental snapshots of the solution. • Important if you are lost or fall behind (pickup from the last version). • Only new additions are commented.
Versions 1 And 2: Piece 61
V1.1 Store whether a piece is a nought (if it isn’t we know it’s a cross). V1.2 When I make a piece (when it’s a new turn, and a piece appears), I want to know whether the last piece played was a cross. If it was, I know that I want this piece to be a nought. V1.3 When I print a piece, I want to see whether it’s a nought or a cross. V2.1 When I compare one piece to another, I want to know whether it’s a nought. PIECE: REQUIREMENTS SUMMARY 62Version 1, Piece.java public class Piece {
Store whether a piece is a nought (if it isn’t we know it’s a cross). PIECE: REQUIREMENT V1.1 63Version 1, Piece.java, Constructor public class Piece { private boolean isNought; public Piece(boolean isNought) { this.isNought = isNought; } }
When I make a piece (when it’s a new turn, and a piece appears), I want to know whether the last piece played was a cross. If it was, I know that I want this piece to be a nought. PIECE: REQUIREMENT V1.2 64Version 1, Piece.java, Second constructor public Piece( Piece lastPiecePlayed ) { isNought = !lastPiecePlayed.isNought; } We effectively `flip’ the piece.
REMEMBER: MODELLING A BANK ACCOUNT (7): TRANSFER 65 public class BankAccount { private double balance; public void deposit(double amount) { balance = balance + amount; } public void printBalance() { … } public void withdraw(double amount) { balance = balance - amount; } public void transfer(BankAccount otherAccount, double amount) { withdraw(amount); otherAccount.deposit(amount); } } The notion of a method of a class accepting objects of that class itself should be a familiar one.
Looking at the way we’ve interacted with the isNought field here, we can see that we’ve called it directly through a Piece object, despite that field being private. This may seem strange, given what we know about private fields, but is permitted by the compiler when a class attempts to access a field of an object of itself. Because this interaction happens inside the class itself, this class is still in control of how the values in this field are manipulated, so encapsulation is not compromised. FLEXIBLE ENCAPSULATION 66 public Piece( Piece lastPiecePlayed ) { isNought = !lastPiecePlayed.isNought; } This is also why we can call private static fields with a class prefix from inside that class.
😴 If you print an object (including an array) directly, Java will print the name of that object’s class, and a code derived from the object’s memory address (more next semester). However, it is quite intuitive to want to print objects, especially if they represent entities that are themselves values (such as a coordinate). PRINTING OBJECTS (1) 67 Some of you will have already experimented with what occurs if an object is printed. Coordinates planeCoordinates = new Coordinates(10, 30); System.out.println(planeCoordinates);
😴 Because it is intuitive to want to be able to print an object, and to get some information back from it by doing so, Java provides us with a way to make our objects printable. That is, when you pass an object to a print statement, the JVM will look for a method called toString in that object, and will call it, effectively transforming the object into whatever is returned by the toString method. THE TOSTRING() METHOD (1) 68
😴 THE TOSTRING() METHOD (2) 69 public String toString() { return "(" + x + "," + y + ")"; } In the coordinates class: Coordinates planeCoordinates = new Coordinates(10, 30); System.out.println(planeCoordinates); In a driving class: You cannot supply any parameters. You must return a String.
😴 Lots of open questions: • How does the JVM know to look for a method called toString? • Why are details of memory printed if we don’t add a toString method ourselves? • What happens if we don’t return a String? • What happens if we add parameters? THE TOSTRING() METHOD (3) 70
When I print a piece, I want to see whether it’s a nought or a cross. PIECE: REQUIREMENT V1.3 71Version 1, Piece.java, toString public String toString() { if (isNought) { return "O"; } else { return "X"; } }
public class Main { public static void main(String[] args) { Piece pieceOne = new Piece(true); System.out.println(pieceOne); Piece pieceTwo = new Piece(pieceOne); System.out.println(pieceTwo); } } VERSION 1: TESTING 72Version 1, Main.java, main method
When I compare one piece to another, I want to know whether it’s a nought. PIECE: REQUIREMENT V2.1 73Version 2, Piece.java, matches public boolean matches( Piece otherPiece ) { if ( otherPiece == null ) return false; return otherPiece.isNought == isNought; } If the other piece is null, we already know they can’t be equal.
VERSION 2: TESTING 74 public class Main { public static void main(String[] args) { Piece pieceOne = new Piece(true); System.out.println(pieceOne); Piece pieceTwo = new Piece(pieceOne); System.out.println(pieceTwo); System.out.println(pieceOne.matches(pieceTwo)); Piece pieceThree = new Piece(true); System.out.println(pieceOne.matches(pieceThree)); } } Version 2, Main.java, main method
PIECE, OVERVIEW. 75
Version 3: Piece And Board 76
PREFACE: MODELLING A 2D SPACE (1) 77 0 1 2 0 1 2 Piece[] row0 = { new Piece(true), null, new Piece(false) }; Piece[] row1 = { new Piece(true), new Piece(true), new Piece(false) }; Piece[] row2 = { new Piece(true), new Piece(false), null };
😴 There are several other phenomena that show that we’re simplifying things at this stage. REMEMBER: INTERACTING WITH A CLASS COPY WITHOUT A VARIABLE 78 new MartinPrinter().printMartin(); copyOfMartinPrinter.printMartin(); For example, we can interact with copies of a class without placing that copy into a variable. password.length() "mypassword".length();
PREFACE: MODELLING A 2D SPACE (2) 79 0 1 2 0 1 2 new Piece(true) null new Piece(false) new Piece(true) new Piece(true) new Piece(false) Values we want to deal with collectively are once again separated. new Piece(true) null new Piece(false)
PREFACE: MODELLING A 2D SPACE (3) 80 0 1 2 0 1 2 new Piece(true) null new Piece(false new Piece(true) new Piece(true) new Piece(false) new Piece(true) null new Piece(false) Grouping things together among two dimensions.
PREFACE: MODELLING A 2D SPACE (4) 81 0 1 2 0 1 2 Piece[][] rows = { { new Piece(true), null, new Piece(false) }, { new Piece(true), new Piece(true), new Piece(false) }, { new Piece(true), new Piece(false), null }, }; One outer array, three inner arrays; each index of the outer array is itself an array; there arrays in an array with three positions.
PREFACE: MODELLING A 2D SPACE (5) 82 0 1 2 0 1 2 Piece[][] rows = new Piece[3][3]; rows[0][0] = new Piece(true); rows[0][1] = null; rows[0][2] = new Piece(false); Column Row
V3.1 Create an empty board V3.2 If I want to start a new game, the board should be cleared. BOARD: REQUIREMENTS SUMMARY 83Version 3, Game.java public class Game {
V3.1 Create an empty board. BOARD: REQUIREMENT V3.1 84Version 3, Game.java, constructor public class Game { private Piece[][] board; public Game() { board = new Piece[3][3]; }
V3.2 If I want to start a new game, the board should be cleared. BOARD: REQUIREMENT V3.2 85Version 3, Game.java, newGame public void newGame() { for ( int row = 0; row < board.length; row++ ) { for ( int column = 0; column < board[0].length; column++ ) { board[row][column] = null; } } } We could simply overwrite the board array with a new array, but this feels a little brute force. The number of columns in the first row.
🙈 INTERACTING WITH A 2D ARRAY: NESTED LOOPS 86 Value of row Value of column board[row][column] 0 0 1 board[0][0] 0 (Stays fixed) 1 board[0][1] 0 (Stays fixed) 2 board[0][2] 1 (Increments) 0 (Restarts) board[1][0] 0 1 2 0 1 2 for ( int row = 0; row < board.length; row++ ) { for ( int column = 0; column < board[0].length; column++ ) { board[row][column] = null; } }
PIECE AND GAME, OVERVIEW. 87
We do not tackle the modelling problem directly. We identify individual objects first, and then use them as the building blocks for our complete program. We solve what we can at first while ignoring other parts, and then return later. We keep iterating and refining the solution. This process cannot be taught. It is learnt by practise. THE PROCESS OF PROBLEM SOLVING 88
There is no content in the remaining requirements that will be directly assessed. But, you are encouraged to follow the remainder of the Noughts and Crosses exercise, to indirectly prepare yourself for future coursework tasks, and the examination. 89
Version 4: Move 90
V4.1 Store the column and the row in which the move took place. V4.2 Provide the ability to return the row and return the column. V4.3 Check whether the row and column in the move are valid (i.e. on the board). PIECE: REQUIREMENTS SUMMARY 91Version 4, Move.java public class Move {
PIECE: REQUIREMENT 4.1 92Version 4, Move.java, constructor Store the column and the row in which the move took place. public class Move { private int row; private int column; public Move(int row, int column) { this.row = row; this.column = column; }
PIECE: REQUIREMENT 4.2 93Version 4, Move.java, getRow and getColumn Provide the ability to return the row and return the column. public int getRow() { return row; } public int getColumn() { return column; }
PIECE: REQUIREMENT 4.3 94Version 4, Move.java, isValid Check whether the row and column in the move are valid (i.e. on the board). public boolean isValid() { return row >= 0 && row < 3 && column >=0 && column < 3; } The position of the piece has to be on the board in order for this move to be valid.
public static void main(String[] args) { Move move1 = new Move(1, 1); System.out.println(move1.isValid()); Move move2 = new Move(2, 1); System.out.println(move2.isValid()); Move move3 = new Move(100, 100); System.out.println(move3.isValid()); } VERSION 4: TESTING 95Version 4, Main.java, main
Version 5: Playing The Game 96
PLAYING THE GAME: REQUIREMENTS 97Version 5, Main.java, main public static void main(String[] args) { Game g = new Game(); Piece piece = new Piece(true); Move firstMove = new Move(1, 1); } If I want to play a piece in the middle square of the board, this is the expressivity I have to do so, currently. Really though I want to be able to add a piece to the board (V5.1).
PLAYING THE GAME: REQUIREMENT 5.1 98 Supply a method that, given a move and a piece, makes that move by adding the piece to the board. public void play(Piece piece, Move move) { board[move.getRow()][move.getColumn()] = piece; } Version 5, Game.java, play
VERSION 5: TESTING 99 public void printBoard() { for ( int row = 0; row < board.length; row++ ) { for ( int column = 0; column < board[0].length; column++ ) { System.out.println(board[row][column]); } } } public static void main(String[] args) { Game game = new Game(); Piece piece = new Piece(true); Move firstMove = new Move(1, 1); game.play(piece, firstMove); } Version 5, Game.java printBoard and Main.java, main
PIECE, GAME AND MOVE, OVERVIEW. 100
Version 6: Rules Of The Game 101
We don’t want players to be able to add pieces to the board as they wish. Instead, this should be subject to restriction. I should only be able to play a piece if the position I want to fill is on the board (Done: V4.3) and if the position I want to fill is empty (New: V6.1). RULES OF THE GAME: REQUIREMENTS 102
RULES OF THE GAME: REQUIREMENT 6.1 (1) 103Version 6, Game.java, canPlay Check whether a position on the board is empty. public boolean canPlay(Move m) { return board[m.getRow()][m.getColumn()] == null; } Given a move, this method will tell me whether the row and column in that move refer to an empty position on the board.
PLAYING THE GAME: REQUIREMENT 6.1 (2) 104 I should only be able to play a piece if the position I want to fill is on the board and if the position I want to fill is empty. Version 6, Game.java, play public boolean play(Piece piece, Move move) { if ( move.isValid() && canPlay(move) ) { board[move.getRow()][move.getColumn()] = piece; return true; } else { return false; } }
public class Main { public static void main(String[] args) { Game game = new Game(); Piece piece = new Piece(true); Move firstMove = new Move(1, 1); System.out.println(game.play(piece, firstMove)); } } VERSION 6: TESTING 105Version 6, Main.java, main
Version 7: Involving The User 106
We want to evolve our game, such that a user is asked for input, and if their move is not valid, they are asked for their input again (V7.1). INVOLVING THE USER: CURRENT TIMELINE 107 Game game = new Game(); Piece piece = new Piece(true); Move firstMove = new Move(100, 100); System.out.println(game.play(piece, firstMove)); END
INVOLVING THE USER: REQUIREMENT 7.1 (FIRST PASS) 108 We want to evolve our game, such that a user is asked for input, and if their move is not valid, they are asked for their input again. Scanner in = new Scanner(System.in); Game game = new Game(); Piece piece = new Piece(true); boolean valid = false; int row = in.nextInt(); int column = in .nextInt(); Move move = new Move(row, column); valid = game.play(piece, move); System.out.println(valid); in.close(); Version 7, Main.java, main
INVOLVING THE USER: REQUIREMENT 7.1 109 Scanner in = new Scanner(System.in); Game game = new Game(); Piece piece = new Piece(true); boolean valid = false; while (!valid) { int row = in.nextInt(); int column = in .nextInt(); Move move = new Move(row, column); valid = game.play(piece, move); System.out.println(valid); We keep asking a user for input while their suggested move is not valid. Version 7, Main.java, main
Version 8: Game Timeline 110
Our current timeline only allows for one valid move. We obviously want to allow for more than one move. For simplicity, let’s first imagine an infinite game. GAME TIMELINE: CURRENT TIMELINE 111 Scanner in = new Scanner(System.in); Game game = new Game(); Piece piece = new Piece(true); boolean valid = false; while (!valid) { int row = in.nextInt(); int column = in .nextInt(); Move move = new Move(row, column); valid = game.play(piece, move); System.out.println(valid);
Piece piece = new Piece(true); while (true) { System.out.println("Next move for: " + piece); boolean valid = false; while (!valid) { int row = in.nextInt(); int column = in .nextInt(); Move move = new Move(row, column); valid = game.play(piece, move); } piece = new Piece(piece); } V8.1 Add the capability for continuous moves, with alternating pieces. GAME TIMELINE: REQUIREMENTS 112 Version 8, Main.java, main
Version 9: Ending The Game (Part 1) 113
We don’t want to keep asking for pieces forever, which is the functionality we have currently. Instead, we want to end a game in the following circumstances: • (Part 1) When every cell is filled (i.e. after 9 moves). • (Part 2) When someone wins, with the following winning conditions: full column, full row, full forward diagonal or full backward diagonal ENDING A GAME: OVERVIEW 114
V9.1 Add a counter to a game that counts the number of moves made, and is able to report that a game is finished when that counter exceeds 9. V9.2 A game should only continue while it is not finished. ENDING A GAME, PART 1: REQUIREMENTS 115
ENDING A GAME, PART 1: REQUIREMENT V9.1 (1) 116 private int plays; private boolean finished; public boolean play(Piece piece, Move move) { if ( move.isValid() && canPlay(move) ) { board[move.getRow()][move.getColumn()] = piece; plays++; if ( plays == 9 ) finished = true; return true; } else { return false; } } Version 9, Game.java, play V9.1 Add a counter to a game that counts the number of moves made, and is able to report that a game is finished when that counter exceeds 9.
V9.1 Add a counter to a game that counts the number of moves made, and is able to report that a game is finished when that counter exceeds 9. ENDING A GAME, PART 1: REQUIREMENT V9.1 (2) 117 public boolean gameOver() { return finished; } plays = 0; finished = false; Version 9, Game.java, gameOver and newGame
while ( !game.gameOver() ) { System.out.println("Next move for: " + piece); boolean valid = false; while (!valid) { int row = in.nextInt(); int column = in .nextInt(); Move move = new Move(row, column); valid = game.play(piece, move); } piece = new Piece(piece); } V9.2 A game should only continue while it is not finished. ENDING A GAME, PART 1: REQUIREMENT V9.2 118 Version 9, Main.java, main
Version 10: Ending The Game (Part 2) 119
We don’t want to keep asking for pieces forever, which is the functionality we have currently. Instead, we want to end a game in the following circumstances: • (Part 1) When every cell is filled (i.e. after 9 moves). • (Part 2) When someone wins, with the following winning conditions: full column, full row, full forward diagonal or full backward diagonal ENDING A GAME: OVERVIEW 120
public boolean play(Piece piece, Move move) { if ( move.isValid() && canPlay(move) ) { board[move.getRow()][move.getColumn()] = piece; plays++; checkWinner(); if ( plays == 9 ) finished = true; return true; } else { return false; } } ENDING A GAME, PART 2: SCAFFOLDING 121 public void checkWinner() { } We know that we want to check for a winner after each play. Methods provide us with the ability to scaffold for future functionality.Version 10, Game.java, play
Let’s first imagine that the only way to win a game of noughts and crosses is to fill the first column (column 0, as shown). We could add another method to our code to facilitate a check for this winning condition: ENDING A GAME, PART 2: ONE FULL COLUMN (AND MORE SCAFFOLDING) 122 public void checkWinner() { checkColumn(); } Version 10, Game.java, checkWinner and checkColumn public void checkColumn() { }
V10.1 Complete the functionality in checkColumn to discern whether the first column contains a winning move by either player, and set a variable to indicate which player this is, should a winning move occur. V10.2 Change this method to determine whether any column contains a winning move by a player, and thus our notion of how a game is won. V10.3 When a game is over, if nobody has won (because all the moves have been exhausted), then print this, otherwise, print the winner. ENDING A GAME, PART 2: REQUIREMENTS 123
ENDING A GAME, PART 2: REQUIREMENT V10.1 124 private Piece winner; public void checkColumn() { Piece extractedFirstPiece = board[0][0]; if ( extractedFirstPiece != null && extractedFirstPiece.matches(board[1][0]) && extractedFirstPiece.matches(board[2][0])) { finished = true; winner = extractedFirstPiece; } } V10.1 Write a method that checks whether the first column contains a winning move by either player, and sets a variable to indicate which player this is, should this occur. We use null here and short- circuit evaluation to protect against null references. 0 1 2 0 1 2 Version 10, Game.java, checkColumn
🙈 ENDING A GAME, PART 2: REQUIREMENT V10.2 125 0 1 2 0 1 2 public void checkColumn(int column) { Piece extractedFirstPiece = board[0][column]; if ( extractedFirstPiece != null && extractedFirstPiece.matches(board[1][column]) && extractedFirstPiece.matches(board[2][column])) { finished = true; winner = extractedFirstPiece; } } public void checkWinner() { for ( int column = 0; column < 3; column++ ) { checkColumn(column); } } public Piece getResult() { return winner; } V10.2 Extend this method to determine whether any column contains a winning move by a player, and thus our notion of how a game is won. Version 10, Game.java, checkColumn, checkWinner and getResult
ENDING A GAME, PART 2: REQUIREMENT V10.3 126 V10.3 When a game is over, if nobody has won (because all the moves have been exhausted), then print this, otherwise, print the winner. System.out.println( game + "n Game Over."); if ( game.getResult() == null ) { System.out.println("Nobody won :-("); } else { System.out.println(game.getResult() + " won :-)"); } Version 10, Main.java, main
Playing The Game… 127
MAKING THINGS PRETTY 128 public String toString() { String output = "+---+---+---+n"; for ( int row = 0; row < 3; row++ ) { output = output + "| "; for( int column = 0; column < 3; column++ ) { if ( board[row][column] == null ) { output = output + " | "; } else { output = output + board[row][column] + " | "; } } output = output + "n+---+---+---+n"; } return output; } Version 10, Game.java, toString
PLAYING A GAME 129 +---+---+---+ | | | | +---+---+---+ | | | | +---+---+---+ | | | | +---+---+---+ Next move for O: 0 0 +---+---+---+ | O | | | +---+---+---+ | | | | +---+---+---+ | | | | +---+---+---+ Next move for X: 0 2 +---+---+---+ | O | | X | +---+---+---+ | | | | +---+---+---+ | | | | +---+---+---+ Next move for O: 1 0 +---+---+---+ | O | | X | +---+---+---+ | O | | | +---+---+---+ | | | | +---+---+---+ Next move for X: 1 2 +---+---+---+ | O | | X | +---+---+---+ | O | | X | +---+---+---+ | | | | +---+---+---+ Next move for O: 2 0 +---+---+---+ | O | | X | +---+---+---+ | O | | X | +---+---+---+ | O | | | +---+---+---+ O won :-)
🖥 The game showed in the previous slide is still only based on detecting whether either player completes an entire column. In the laboratory, complete the functionality, so that the remaining winning conditions can be detected: • A row is completed. • A forward diagonal is completed. • A backward diagonal is completed. You should add to the checkWinner method, and try and consider the efficiency of your solution: when do we no longer need to check for a winner? COMPLETING THE GAME LOGIC 130Topic7-2
Dr. Martin Chapman programming@kcl.ac.uk martinchapman.co.uk/teaching Programming Practice and Applications (4CCS1PPA) Topic 7: Arrays These slides will be available on KEATS, but will be subject to ongoing amendments. Therefore, please always download a new version of these slides when approaching an assessed piece of work, or when preparing for a written assessment. 131 Thursday 17th November

Programming in Java: Arrays

  • 1.
    Dr. Martin Chapman programming@kcl.ac.uk martinchapman.co.uk/teaching ProgrammingPractice and Applications (4CCS1PPA) Topic 7: Arrays Q: What is the non-technical definition of an array? 1 Thursday 17th November
  • 2.
    To understand howto employ the array data structure in our programs. • To build a working representation of the game of Noughts and Crosses. ARRAYS: OBJECTIVES 2
  • 3.
    Given this setof numbers — 32, 54, 68 and 30 — write a program that stores these numbers, and then examines each of them and prints the word YES if the number is greater than the average (46). EXERCISE: HANDLING SETS OF NUMBERS (1) 3
  • 4.
    EXERCISE: HANDLING SETSOF NUMBERS (2) 4 int numberOne, numberTwo, numberThree, numberFour; numberOne = 32; if ( numberOne > 46 ) { System.out.println("YES"); } numberTwo = 54; if ( numberTwo > 46 ) System.out.println("YES"); numberThree = 68; if ( numberThree > 46 ) System.out.println("YES"); numberFour = 30; if ( numberFour > 46 ) System.out.println("YES"); 32 54 68 30
  • 5.
    Ideally, rather thanhaving to interact with each stored integer individually, we want to interact with a single set of connected integers. This necessitates a single label for all the stored values. EXERCISE: HANDLING SETS OF NUMBERS (3) 5 32 54 68 3030 68 54 32
  • 6.
    An array isthe answer. • Dictionary definition: ‘To arrange a group of things in a particular order’. An array is a type of variable with multiple slots. Each slot can hold a different value. The values in an array must all be of the same type. ARRAYS (1) 6 30 68 54 32
  • 7.
    `A array isa type of variable’ ARRAYS (2) 7 int numberOne, numberTwo, numberThree, numberFour; `with multiple slots.’ int[] numbers int[] numbers = new int[4]; `My numbers variable has 4 slots’ `Each slot can hold a different value.’ numberOne = 32; numberTwo = 54; numberThree = 68; numberFour = 30; numbers[0] = 32; numbers[1] = 54; numbers[2] = 68; numbers[3] = 30; We place square brackets after a type to indicate that we want to store a sequence of values of this type.
  • 8.
    Create an array… ARRAYS(3): CREATE AND STORE (SUMMARY) 8 …and then store things in that array. int[] numbers = new int[4]; numbers[0] = 32; numbers[1] = 54; numbers[2] = 68; numbers[3] = 30; 32 54 67 2930 68 54 32 [0] [1] [2] [3] The length of the array differs from the last index.
  • 9.
    Because of thisbreakdown, we can imagine each character in a string as having an index. • This means that there is a distinct difference between the length of a string and the last index. REMEMBER: INDEX VS. LENGTH 9 ‘M’,‘a’,‘r’,‘t’,‘i’,‘n’ Character 5 Length = 6. We can leverage this idea in order to approach our current problem.
  • 10.
    We could combinethe steps we saw previously in order to declare and initialise an array in a single line. • A similar principle as a single line declaration and initialisation for a normal variable. • Thus, we would need to know the values we want in that array immediately. This would give us an array of a size equal to the number of values. ARRAYS (3): CREATE AND STORE (SINGLE LINE) 10 int[] numbers = { 32, 54, 68, 30 };
  • 11.
    FIXED ARRAY SIZE 11 int[]numbers = new int[4]; numbers[0] = 32; numbers[1] = 54; numbers[2] = 68; numbers[3] = 30; When we specify the initial size of an array, this size is fixed, such that if we try and write to a non-existent index, the following occurs: numbers[4] = 52;
  • 12.
    😴 Note that thiserror doesn’t occur at compile time (after the compiler is run), but instead at run time (after the virtual machine is run). • The compiler is (necessarily) too simplistic to catch this issue at compile time. • It also isn’t always possible to identify this type of error at compile time. For example, access to, or the size of, an array may occur as the result of user input. • We also saw an InputMismatchException runtime error in Topic 6. • We’ll return to these ideas. ERROR: INDEX OUT OF BOUNDS EXCEPTION 12
  • 13.
    We first needto work out how to determine the length of an array. HOW DO WE AVOID A NON-EXISTENT INDEX IN AN ARRAY BEING WRITTEN TO? (1) 13 int[] numbers = new int[4]; System.out.println("The length of the " + "numbers array is: " + );numbers.length If we want to split a string over two lines, we need to concatenate.
  • 14.
    HOW DO WEAVOID A NON-EXISTENT INDEX IN AN ARRAY BEING WRITTEN TO? (2) 14 int i = 4, value = 52; int[] numbers = new int[4]; if ( 0 <= i && i < ) { numbers[i] = value; } numbers.length We can then combine this information with a conditional statement, to ensure that a non-existent index in an array is not written to. This test would become particularly important if we weren’t (directly) in control of the value in i.
  • 15.
    We’ve seen theword length used in two places now. • To determine the length of a string. ASIDE: ARRAY LENGTH VS. STRING LENGTH 15 • This is a method call (brackets). • To determine the length of an array. password.length() numbers.length • This resembles a reference to a field (no brackets). • More shortly.
  • 16.
    In order toget data back from an array, we simply reference the index of the data that we want. HOW DO WE GET DATA BACK FROM AN ARRAY? 16 int[] numbers = new int[4]; numbers[0] = 32; numbers[1] = 54; numbers[2] = 68; numbers[3] = 30; System.out.println("The value in slot 2 is: " + numbers[2]);
  • 17.
    Given this setof numbers — 32, 54, 68 and 30 — write a program that stores these numbers, and then examines each of them and prints the word YES if the number is greater than the average (46). Hint: Think about how we might extend the idea of using index and length, as seen in Slide 14. EXERCISE: BACK TO HANDLING SETS OF NUMBERS (1) 17
  • 18.
    REMEMBER: LEVERAGING THELOOP INDEX (3) 18 public class NumberPrinter { public void printNumber(int num) { System.out.println("+------+"); System.out.println("|" + num + "|"); System.out.println("+------+"); } } public class Driver { public static void main(String[] args) { NumberPrinter numberPrinter = new NumberPrinter(); for ( int i = 1; i < 4; i++ ) { numberPrinter.printNumber(i); } } } As the loop index is just a variable declaration, it can be referenced inside the loop.
  • 19.
    EXERCISE: BACK TOHANDLING SETS OF NUMBERS (2) 19 int[] numbers = new int[4]; numbers[0] = 32; numbers[1] = 54; numbers[2] = 68; numbers[3] = 30; for ( int i = 0; i < numbers.length; i++ ) { if ( numbers[i] > 46 ) System.out.println("YES"); } We use our knowledge of loop bounds to carefully control how we access each index. We could shorten this to a single line initialisation and declaration.
  • 20.
    Let’s imagine wewant to extend our number checker to instead store an arbitrary amount of numbers from a user, and then check which are over the average. CAPTURING AN UNKNOWN AMOUNT OF USER INPUT (1) 20
  • 21.
    REMEMBER: TRACKING AUSER’S CALORIES (3) 21 import java.util.Scanner; public class CalorieTracker { public static void main(String[] args) { Scanner in = new Scanner(System.in); Person person = new Person(); System.out.println("Enter the calories in your starter:"); Dish starter = new Dish(); starter.setCalories(in.nextInt()) Every time we invoke the nextInt method, our program will stop and wait for another token of user input.
  • 22.
    CAPTURING AN UNKNOWNAMOUNT OF USER INPUT (2) 22 import java.util.Scanner; Scanner in = new Scanner(System.in); in.nextInt(); in.close(); int[] numbers = new int[ ]; for ( int i = 0; i < ; i++ ) { numbers[i] = } What are our options here? ? ? Let’s fill in as much of the solution as we can: Familiar scanner syntax. Familiar array syntax.
  • 23.
    OPTION 1: LETTHE USER SPECIFY A MAXIMUM 23 import java.util.Scanner; Scanner in = new Scanner(System.in); in.nextInt(); in.close(); int[] numbers = new int[ ]; for ( int i = 0; i < ; i++ ) { numbers[i] = } What if the user themselves is unsure? in.nextInt() numbers.length
  • 24.
    OPTION 2: SPECIFYA MAXIMUM FOR THE USER (1) 24 import java.util.Scanner; Scanner in = new Scanner(System.in); in.nextInt(); in.close(); int[] numbers = new int[ ]; for ( int i = 0; i < ; i++ ) { numbers[i] = } 100 numbers.length
  • 25.
    OPTION 2: SPECIFYA MAXIMUM FOR THE USER (2) 25 import java.util.Scanner; Scanner in = new Scanner(System.in); in.nextInt(); in.close(); int[] numbers = new int[ ]; for ( int i = 0; i < ; i++ ) { numbers[i] = } 100 numbers.length What if someone wants to enter less than 100 numbers?
  • 26.
    OPTION 2: SPECIFYA MAXIMUM FOR THE USER (3) 26 import java.util.Scanner; Scanner in = new Scanner(System.in); in.nextInt(); in.close(); int[] numbers = new int[ ]; while ( in.hasNextInt() ) { numbers[i] = } 100 What if someone wants to enter less than 100 numbers? How do we know where to place the next number? We could keep looping while there are still integers to read. System.out.println("Enter N numbers." + "Type 'done' to finish.");
  • 27.
    This problem stemsfrom the fact that arrays (initially) have to be a fixed size. Therefore, if a programmer wants to account for an unknown number of input values (such as input coming from a user), they have to specify a large fixed size (like we have done), and prepare for this array to be partially filled with values. Storing values in a partially filled array requires an extra step… PARTIALLY FILLED ARRAYS (1) 27
  • 28.
    PARTIALLY FILLED ARRAYS(2) 28 Scanner in = new Scanner(System.in); int[] numbers = new int[100]; while ( in.hasNextInt() ) { numbers[ ] = in.nextInt(); } in.close(); import java.util.Scanner; int elements = 0; elements++ We define an additional variable to track the next free slot in the array. Every time we add an item to the array, we increment this variable.
  • 29.
    PARTIALLY FILLED ARRAYS(3) 29 If our user intends to enter a large amount of numbers… …then eventually the elements variable may cause problems.
  • 30.
    PARTIALLY FILLED ARRAYS(4) 30 Scanner in = new Scanner(System.in); int[] numbers = new int[100]; while ( in.hasNextInt() && ) { numbers[ ] = in.nextInt(); } in.close(); import java.util.Scanner; int elements = 0; elements++ elements < numbers.length We can also use our element index variable to check whether it’s possible to continue adding items to the array.
  • 31.
    Once a userhas finished inputting their numbers, we can examine what they have entered by again using our elements variable. ACCESSING DATA IN A PARTIALLY FILLED ARRAY (1) 31 for ( int i = 0; i < elements; i++) { total += numbers[i]; } for ( int i = 0; i < elements; i++) { if ( numbers[i] > total / elements ) System.out.println("YES"); } (double) Length is no longer useful because it refers to the length of the array, not the number of items in it.
  • 32.
    There is analternative here, something called the for each loop (originally mentioned in Topic 5): ACCESSING DATA IN A PARTIALLY FILLED ARRAY (2): FOR EACH (1) 32 for ( int number : numbers ) { total += number; } 30 68 54 32 Each number is automatically taken from the array and placed into a temporary variable, which can be referenced in the loop. Changes to this variable do not affect the array. The loop ends when all the items in the array have been examined.
  • 33.
    I almost alwaysuse the for each loop. • A relatively new construct, hence the existence of the more traditional index approach. • Does limit flexibility slightly, given the lack of an index value. ACCESSING DATA IN A PARTIALLY FILLED ARRAY (3) 33 for ( int number : numbers ) { total += number; } for ( int number : numbers ) { if ( number > total / elements ) System.out.println("YES"); } (double)
  • 34.
    If we wereto fill partially fill an array with a certain number of values, but then accidentally access an index that was within the size of the array, but not manually filled, what would happen? • We would not be given an error, but instead a zero would be printed. • Arrays of a primitive type contain default values. ACCESSING DATA IN A PARTIALLY FILLED ARRAY (4) 34 int[] numbers = new int[100]; for ( int i = 0; i < 50; i++) { numbers[ i ] = i; } System.out.println(numbers[50]);
  • 35.
    Recall that Ialso described objects as multiple slot variables in Topic 4, as I have with arrays, suggesting some connection between the two. • The fact that the slots in an integer array are given default values, also suggests some connection to the fields of an object. • As does the fact that the default values in an array, when an array is of a primitive type, are the same as those in the fields of an object, when those fields are of the same primitive type. Arrays are indeed objects (albeit a special type). ARRAYS VS. OBJECTS (1) 35
  • 36.
    The array typesthemselves (e.g. int[], boolean[]) aren’t physical classes, but instead simulated runtime classes provided to us by the JVM, that have certain properties that are similar to those demonstrated by actual classes. • The use of the new command. • New copies to store different data. • Default field values (mentioned). • Public (immutable) fields. ARRAYS VS. OBJECTS (2) 36 numbers.length int[] numbers = new int[4];
  • 37.
    Recall that inTopic 4 we determined that primitive types can be replaced with class types. This idea doesn’t stop with arrays. ARRAYS OF OBJECTS (1) 37 We can, as we might expect, connect a set of objects in an array. Indeed, we’ve had an example of this right under our noses for quite some time: int[] numbers = new int[4]; private Dish[] starterMainDessert = new Dish[3]; String[] args
  • 38.
    We now knowa even more about main. • We know that main can never return anything (where would it go anyway?) • This is a parameter, so main must accept some information. • But unfortunately, we still aren’t in a position to discuss what is passed to the main method. REMEMBER: BACK TO MAIN 38 public static void main(String[] args) { • We know that main has to be visible from outside the class, so that it can be run by the JVM (Topic 4). • We know that main can be accessed without having to make an object of our driver class, which makes things easier for the JVM.
  • 39.
    We now knoweverything about main. • We know that main can never return anything (where would it go anyway?) • This is a String array, which is passed to the main method. • We know that main has to be visible from outside the class, so that it can be run by the JVM (Topic 4). • We know that main can be accessed without having to make an object of our driver class, which makes things easier for the JVM (Topic 6). REMEMBER: BACK TO MAIN 39 public static void main(String[] args) {
  • 40.
    When we usesoftware such as a video converter to convert files, or we use a word processor, we input our source files (.mp3 file, .docx file) into the software using the GUI. • We might right-click on the .docx file and select `open with Microsoft Word’. When we use software such as the Java compiler and the Java virtual machine to compile and run programs, we input our source files using a terminal (a command line interface). • It is possible to run compiled (Java) programs from the GUI, but it requires a more complex compilation configuration. REMEMBER: USING THE COMPILER AND THE JAVA VIRTUAL MACHINE 40
  • 41.
    Each token wewrite after the name of our compiled program when we pass it to the Java virtual machine, will be passed, token-by-token, to each index of the args array. COMMAND LINE ARGUMENTS (1) 41 public class CommandLineArguments { public static void main(String[] args) { System.out.println(args.length); for ( String argument: args ) { System.out.println(argument); } } }
  • 42.
    COMMAND LINE ARGUMENTS(2) 42 32 54 67 29 [0] [1] [2] [3] [4] Hello My Name Is Martin Each token we write after the name of our compiled program when we pass it to the Java virtual machine, will be passed, token-by-token, to each index of the args array.
  • 43.
    A piece ofdata that we pass to a program via the command line is referred to as an argument. • Hence the abbreviation args (although this can be changed). • An argument, in the literal sense, is a piece of information from which another action may be inferred. • We also pass arguments (data) to parameters when we call methods. COMMAND LINE ARGUMENTS (3) 43
  • 44.
    Command line argumentsgive us another way to take user input. As we can only do this once, this style of input only really applies to simple programs, or to setting flags that affect the overall operation of the program. To take input more than once, and in an arguably more user friendly manner (with the inclusion of print statements), we need to use the Scanner class, as we saw in Topic 6. SCANNER VS. ARGS (1) 44
  • 45.
    😴 SCANNER VS. ARGS(2): SIMPLE PROGRAMS 45 public class Adder { public static void main(String[] args) { if ( args.length == 2 ) { System.out.println(Integer.parseInt(args[0]) + Integer.parseInt(args[1])); } } } Another Library class enabling the conversion of strings to integers. We will return to look at this class and this idea. Even if we enter numbers into the command line, each token is always treated as a string.
  • 46.
    SCANNER VS. ARGS(3): SETTING FLAGS 46
  • 47.
    REMEMBER: LECTURE EXERCISE:BLOCKCHAIN (4) - MINER AND BLOCKCHAIN 47 public class Miner { public static void main(String[] args) { Block myBlock = new Block(); Blockchain chain = new Blockchain(); chain.addBlock(myBlock); } } public class Blockchain { public void addBlock(Block block) { } } Keep a list of blocks Add the block to a list How do we do this? On to Topic 7! Back to arrays of objects…
  • 48.
    REMEMBER: LECTURE EXERCISE:BLOCKCHAIN (4) - MINER AND BLOCKCHAIN 48 public class Miner { public static void main(String[] args) { Block myBlock = new Block(); Blockchain chain = new Blockchain(); chain.addBlock(myBlock); } } public class Blockchain { private Block[] chain; private int blocks; public Blockchain(int chainLength) { chain = new Block[chainLength]; } public void addBlock(Block block) { chain[blocks++] = block; } }
  • 49.
    😴 Like accessing avariable of a class type when it is a field before it has had a copy of a class assigned to it, accessing an empty index in an array of a class type will result in a NullPointerException at runtime. • We’ve already seen InputMismatchExceptions and ArrayIndexOutOfBoundsExceptions. We will formalise these observations in Topic 10. NULL (1) 49 public class PairBalancer { private Pair toBalance; public PairBalancer(int valueA, int valueB) { toBalance.setValueA(valueA); toBalance.setValueB(valueB); }
  • 50.
    This error mayseem to contradict the idea that fields (and the indices in an array) are given default values. But in reality, there is a default value here, added in automatically for us: NULL (2) 50 public class PairBalancer { private Pair toBalance; public PairBalancer(int valueA, int valueB) { System.out.println(toBalance); }
  • 51.
    😴 The value nullcan be seen as a special literal, placed inside variables of a class type before they are assigned a copy of a class, and thus become objects (under our current understanding). • Again, the true role of null will become clearer when we look at objects in memory. We can use this literal to protect against errors: NULL (3) 51 if ( toBalance != null ) { toBalance.setValueA(valueA); toBalance.setValueB(valueB); }
  • 52.
    In the Javalibrary, you’ll find a class called Arrays. ASIDE: THE ARRAYS UTILITY CLASS 52 The class has a number of useful static methods: int[] hulk = new int[10]; int elements = 0; for ( int i = 0; i < 100; i++ ) { if ( elements == hulk.length ) { hulk = Arrays.copyOf(hulk, hulk.length * 2); } hulk[elements++] = i; } System.out.println(Arrays.toString(hulk)); Double the size of the array as necessary (not particularly neat). Print the content of the array. Open question: what happens if we print the array directly? import java.util.Arrays;
  • 53.
    Given the arrayof numbers we had earlier: LECTURE EXERCISE: LARGEST NUMBER (1) 53 Write a program that identifies the largest number in the list. int[] numbers = { 32, 54, 68, 30 };
  • 54.
    LECTURE EXERCISE: LARGESTNUMBER (2) 54 int largest = numbers[0]; for ( int i = 1; i < numbers.length; i++ ) { if ( numbers[i] > largest ) { largest = numbers[i]; } } System.out.println("Largest: " + largest);
  • 55.
    🖥 Some exercises involvingarrays, for you to try in the laboratories: • Write a method that sums all the values in an array. • Fill an array, of size 50, with random numbers (between 1 and 100). Print out the contents of this array, 10 numbers per line, separating each number with a comma. Finish the whole sequence will a full stop. • With the same array, replace the number in the second index with the number `1337’, and shift every other number up by one place. ARRAY EXERCISES 55Topic7-1
  • 56.
  • 57.
    😴 We’ve already discussed,and seen lots of examples of, how code can be used to model real world phenomena (i.e. a set of objects). Before we model something, we have to make a decision about the classes we want to include in our model. • Typically these are the nouns pertinent to the problem. • We will look more formally at this process next semester. We then build these objects individually, before combining them to complete our model. Let’s look at this process for the game of Noughts and Crosses. IDENTIFYING CLASSES 57
  • 58.
    For the gameof noughts and crosses, I would propose that we look at the following classes: CLASSES IN NOUGHTS AND CROSSES 58And a Main class. Game (or Board) Piece Move
  • 59.
    Following requirements, orthe design decisions made by someone else, is something you will now be familiar with from the assignment briefs. We have a solution in mind, and are using these requirements to guide you towards it. • Only way to structure the collective production of large programs (be this for an assignment, or as part of a lecture). • Some connection with industry, where you will be building to a specification. • Asks you to suspend the why slightly. FOLLOWING REQUIREMENTS (1) 59
  • 60.
    When we followrequirements during a lecture, to produce a solution together, we will do so with the aid of offline versions. You will find these on KEATS. FOLLOWING REQUIREMENTS (2): VERSIONS 60 /** * A class that represents A, B and C. * * Version 1. * * New in this version: * * 1. field D * 2. method E * * @author Martin * */ public class F { • Show you incremental snapshots of the solution. • Important if you are lost or fall behind (pickup from the last version). • Only new additions are commented.
  • 61.
    Versions 1 And2: Piece 61
  • 62.
    V1.1 Store whethera piece is a nought (if it isn’t we know it’s a cross). V1.2 When I make a piece (when it’s a new turn, and a piece appears), I want to know whether the last piece played was a cross. If it was, I know that I want this piece to be a nought. V1.3 When I print a piece, I want to see whether it’s a nought or a cross. V2.1 When I compare one piece to another, I want to know whether it’s a nought. PIECE: REQUIREMENTS SUMMARY 62Version 1, Piece.java public class Piece {
  • 63.
    Store whether apiece is a nought (if it isn’t we know it’s a cross). PIECE: REQUIREMENT V1.1 63Version 1, Piece.java, Constructor public class Piece { private boolean isNought; public Piece(boolean isNought) { this.isNought = isNought; } }
  • 64.
    When I makea piece (when it’s a new turn, and a piece appears), I want to know whether the last piece played was a cross. If it was, I know that I want this piece to be a nought. PIECE: REQUIREMENT V1.2 64Version 1, Piece.java, Second constructor public Piece( Piece lastPiecePlayed ) { isNought = !lastPiecePlayed.isNought; } We effectively `flip’ the piece.
  • 65.
    REMEMBER: MODELLING ABANK ACCOUNT (7): TRANSFER 65 public class BankAccount { private double balance; public void deposit(double amount) { balance = balance + amount; } public void printBalance() { … } public void withdraw(double amount) { balance = balance - amount; } public void transfer(BankAccount otherAccount, double amount) { withdraw(amount); otherAccount.deposit(amount); } } The notion of a method of a class accepting objects of that class itself should be a familiar one.
  • 66.
    Looking at theway we’ve interacted with the isNought field here, we can see that we’ve called it directly through a Piece object, despite that field being private. This may seem strange, given what we know about private fields, but is permitted by the compiler when a class attempts to access a field of an object of itself. Because this interaction happens inside the class itself, this class is still in control of how the values in this field are manipulated, so encapsulation is not compromised. FLEXIBLE ENCAPSULATION 66 public Piece( Piece lastPiecePlayed ) { isNought = !lastPiecePlayed.isNought; } This is also why we can call private static fields with a class prefix from inside that class.
  • 67.
    😴 If you printan object (including an array) directly, Java will print the name of that object’s class, and a code derived from the object’s memory address (more next semester). However, it is quite intuitive to want to print objects, especially if they represent entities that are themselves values (such as a coordinate). PRINTING OBJECTS (1) 67 Some of you will have already experimented with what occurs if an object is printed. Coordinates planeCoordinates = new Coordinates(10, 30); System.out.println(planeCoordinates);
  • 68.
    😴 Because it isintuitive to want to be able to print an object, and to get some information back from it by doing so, Java provides us with a way to make our objects printable. That is, when you pass an object to a print statement, the JVM will look for a method called toString in that object, and will call it, effectively transforming the object into whatever is returned by the toString method. THE TOSTRING() METHOD (1) 68
  • 69.
    😴 THE TOSTRING() METHOD(2) 69 public String toString() { return "(" + x + "," + y + ")"; } In the coordinates class: Coordinates planeCoordinates = new Coordinates(10, 30); System.out.println(planeCoordinates); In a driving class: You cannot supply any parameters. You must return a String.
  • 70.
    😴 Lots of openquestions: • How does the JVM know to look for a method called toString? • Why are details of memory printed if we don’t add a toString method ourselves? • What happens if we don’t return a String? • What happens if we add parameters? THE TOSTRING() METHOD (3) 70
  • 71.
    When I printa piece, I want to see whether it’s a nought or a cross. PIECE: REQUIREMENT V1.3 71Version 1, Piece.java, toString public String toString() { if (isNought) { return "O"; } else { return "X"; } }
  • 72.
    public class Main{ public static void main(String[] args) { Piece pieceOne = new Piece(true); System.out.println(pieceOne); Piece pieceTwo = new Piece(pieceOne); System.out.println(pieceTwo); } } VERSION 1: TESTING 72Version 1, Main.java, main method
  • 73.
    When I compareone piece to another, I want to know whether it’s a nought. PIECE: REQUIREMENT V2.1 73Version 2, Piece.java, matches public boolean matches( Piece otherPiece ) { if ( otherPiece == null ) return false; return otherPiece.isNought == isNought; } If the other piece is null, we already know they can’t be equal.
  • 74.
    VERSION 2: TESTING 74 publicclass Main { public static void main(String[] args) { Piece pieceOne = new Piece(true); System.out.println(pieceOne); Piece pieceTwo = new Piece(pieceOne); System.out.println(pieceTwo); System.out.println(pieceOne.matches(pieceTwo)); Piece pieceThree = new Piece(true); System.out.println(pieceOne.matches(pieceThree)); } } Version 2, Main.java, main method
  • 75.
  • 76.
    Version 3: PieceAnd Board 76
  • 77.
    PREFACE: MODELLING A2D SPACE (1) 77 0 1 2 0 1 2 Piece[] row0 = { new Piece(true), null, new Piece(false) }; Piece[] row1 = { new Piece(true), new Piece(true), new Piece(false) }; Piece[] row2 = { new Piece(true), new Piece(false), null };
  • 78.
    😴 There are severalother phenomena that show that we’re simplifying things at this stage. REMEMBER: INTERACTING WITH A CLASS COPY WITHOUT A VARIABLE 78 new MartinPrinter().printMartin(); copyOfMartinPrinter.printMartin(); For example, we can interact with copies of a class without placing that copy into a variable. password.length() "mypassword".length();
  • 79.
    PREFACE: MODELLING A2D SPACE (2) 79 0 1 2 0 1 2 new Piece(true) null new Piece(false) new Piece(true) new Piece(true) new Piece(false) Values we want to deal with collectively are once again separated. new Piece(true) null new Piece(false)
  • 80.
    PREFACE: MODELLING A2D SPACE (3) 80 0 1 2 0 1 2 new Piece(true) null new Piece(false new Piece(true) new Piece(true) new Piece(false) new Piece(true) null new Piece(false) Grouping things together among two dimensions.
  • 81.
    PREFACE: MODELLING A2D SPACE (4) 81 0 1 2 0 1 2 Piece[][] rows = { { new Piece(true), null, new Piece(false) }, { new Piece(true), new Piece(true), new Piece(false) }, { new Piece(true), new Piece(false), null }, }; One outer array, three inner arrays; each index of the outer array is itself an array; there arrays in an array with three positions.
  • 82.
    PREFACE: MODELLING A2D SPACE (5) 82 0 1 2 0 1 2 Piece[][] rows = new Piece[3][3]; rows[0][0] = new Piece(true); rows[0][1] = null; rows[0][2] = new Piece(false); Column Row
  • 83.
    V3.1 Create anempty board V3.2 If I want to start a new game, the board should be cleared. BOARD: REQUIREMENTS SUMMARY 83Version 3, Game.java public class Game {
  • 84.
    V3.1 Create anempty board. BOARD: REQUIREMENT V3.1 84Version 3, Game.java, constructor public class Game { private Piece[][] board; public Game() { board = new Piece[3][3]; }
  • 85.
    V3.2 If Iwant to start a new game, the board should be cleared. BOARD: REQUIREMENT V3.2 85Version 3, Game.java, newGame public void newGame() { for ( int row = 0; row < board.length; row++ ) { for ( int column = 0; column < board[0].length; column++ ) { board[row][column] = null; } } } We could simply overwrite the board array with a new array, but this feels a little brute force. The number of columns in the first row.
  • 86.
    🙈 INTERACTING WITH A2D ARRAY: NESTED LOOPS 86 Value of row Value of column board[row][column] 0 0 1 board[0][0] 0 (Stays fixed) 1 board[0][1] 0 (Stays fixed) 2 board[0][2] 1 (Increments) 0 (Restarts) board[1][0] 0 1 2 0 1 2 for ( int row = 0; row < board.length; row++ ) { for ( int column = 0; column < board[0].length; column++ ) { board[row][column] = null; } }
  • 87.
    PIECE AND GAME,OVERVIEW. 87
  • 88.
    We do nottackle the modelling problem directly. We identify individual objects first, and then use them as the building blocks for our complete program. We solve what we can at first while ignoring other parts, and then return later. We keep iterating and refining the solution. This process cannot be taught. It is learnt by practise. THE PROCESS OF PROBLEM SOLVING 88
  • 89.
    There is nocontent in the remaining requirements that will be directly assessed. But, you are encouraged to follow the remainder of the Noughts and Crosses exercise, to indirectly prepare yourself for future coursework tasks, and the examination. 89
  • 90.
  • 91.
    V4.1 Store thecolumn and the row in which the move took place. V4.2 Provide the ability to return the row and return the column. V4.3 Check whether the row and column in the move are valid (i.e. on the board). PIECE: REQUIREMENTS SUMMARY 91Version 4, Move.java public class Move {
  • 92.
    PIECE: REQUIREMENT 4.1 92Version4, Move.java, constructor Store the column and the row in which the move took place. public class Move { private int row; private int column; public Move(int row, int column) { this.row = row; this.column = column; }
  • 93.
    PIECE: REQUIREMENT 4.2 93Version4, Move.java, getRow and getColumn Provide the ability to return the row and return the column. public int getRow() { return row; } public int getColumn() { return column; }
  • 94.
    PIECE: REQUIREMENT 4.3 94Version4, Move.java, isValid Check whether the row and column in the move are valid (i.e. on the board). public boolean isValid() { return row >= 0 && row < 3 && column >=0 && column < 3; } The position of the piece has to be on the board in order for this move to be valid.
  • 95.
    public static voidmain(String[] args) { Move move1 = new Move(1, 1); System.out.println(move1.isValid()); Move move2 = new Move(2, 1); System.out.println(move2.isValid()); Move move3 = new Move(100, 100); System.out.println(move3.isValid()); } VERSION 4: TESTING 95Version 4, Main.java, main
  • 96.
    Version 5: PlayingThe Game 96
  • 97.
    PLAYING THE GAME:REQUIREMENTS 97Version 5, Main.java, main public static void main(String[] args) { Game g = new Game(); Piece piece = new Piece(true); Move firstMove = new Move(1, 1); } If I want to play a piece in the middle square of the board, this is the expressivity I have to do so, currently. Really though I want to be able to add a piece to the board (V5.1).
  • 98.
    PLAYING THE GAME:REQUIREMENT 5.1 98 Supply a method that, given a move and a piece, makes that move by adding the piece to the board. public void play(Piece piece, Move move) { board[move.getRow()][move.getColumn()] = piece; } Version 5, Game.java, play
  • 99.
    VERSION 5: TESTING 99 publicvoid printBoard() { for ( int row = 0; row < board.length; row++ ) { for ( int column = 0; column < board[0].length; column++ ) { System.out.println(board[row][column]); } } } public static void main(String[] args) { Game game = new Game(); Piece piece = new Piece(true); Move firstMove = new Move(1, 1); game.play(piece, firstMove); } Version 5, Game.java printBoard and Main.java, main
  • 100.
    PIECE, GAME ANDMOVE, OVERVIEW. 100
  • 101.
    Version 6: RulesOf The Game 101
  • 102.
    We don’t wantplayers to be able to add pieces to the board as they wish. Instead, this should be subject to restriction. I should only be able to play a piece if the position I want to fill is on the board (Done: V4.3) and if the position I want to fill is empty (New: V6.1). RULES OF THE GAME: REQUIREMENTS 102
  • 103.
    RULES OF THEGAME: REQUIREMENT 6.1 (1) 103Version 6, Game.java, canPlay Check whether a position on the board is empty. public boolean canPlay(Move m) { return board[m.getRow()][m.getColumn()] == null; } Given a move, this method will tell me whether the row and column in that move refer to an empty position on the board.
  • 104.
    PLAYING THE GAME:REQUIREMENT 6.1 (2) 104 I should only be able to play a piece if the position I want to fill is on the board and if the position I want to fill is empty. Version 6, Game.java, play public boolean play(Piece piece, Move move) { if ( move.isValid() && canPlay(move) ) { board[move.getRow()][move.getColumn()] = piece; return true; } else { return false; } }
  • 105.
    public class Main{ public static void main(String[] args) { Game game = new Game(); Piece piece = new Piece(true); Move firstMove = new Move(1, 1); System.out.println(game.play(piece, firstMove)); } } VERSION 6: TESTING 105Version 6, Main.java, main
  • 106.
  • 107.
    We want toevolve our game, such that a user is asked for input, and if their move is not valid, they are asked for their input again (V7.1). INVOLVING THE USER: CURRENT TIMELINE 107 Game game = new Game(); Piece piece = new Piece(true); Move firstMove = new Move(100, 100); System.out.println(game.play(piece, firstMove)); END
  • 108.
    INVOLVING THE USER:REQUIREMENT 7.1 (FIRST PASS) 108 We want to evolve our game, such that a user is asked for input, and if their move is not valid, they are asked for their input again. Scanner in = new Scanner(System.in); Game game = new Game(); Piece piece = new Piece(true); boolean valid = false; int row = in.nextInt(); int column = in .nextInt(); Move move = new Move(row, column); valid = game.play(piece, move); System.out.println(valid); in.close(); Version 7, Main.java, main
  • 109.
    INVOLVING THE USER:REQUIREMENT 7.1 109 Scanner in = new Scanner(System.in); Game game = new Game(); Piece piece = new Piece(true); boolean valid = false; while (!valid) { int row = in.nextInt(); int column = in .nextInt(); Move move = new Move(row, column); valid = game.play(piece, move); System.out.println(valid); We keep asking a user for input while their suggested move is not valid. Version 7, Main.java, main
  • 110.
    Version 8: GameTimeline 110
  • 111.
    Our current timeline only allowsfor one valid move. We obviously want to allow for more than one move. For simplicity, let’s first imagine an infinite game. GAME TIMELINE: CURRENT TIMELINE 111 Scanner in = new Scanner(System.in); Game game = new Game(); Piece piece = new Piece(true); boolean valid = false; while (!valid) { int row = in.nextInt(); int column = in .nextInt(); Move move = new Move(row, column); valid = game.play(piece, move); System.out.println(valid);
  • 112.
    Piece piece =new Piece(true); while (true) { System.out.println("Next move for: " + piece); boolean valid = false; while (!valid) { int row = in.nextInt(); int column = in .nextInt(); Move move = new Move(row, column); valid = game.play(piece, move); } piece = new Piece(piece); } V8.1 Add the capability for continuous moves, with alternating pieces. GAME TIMELINE: REQUIREMENTS 112 Version 8, Main.java, main
  • 113.
    Version 9: EndingThe Game (Part 1) 113
  • 114.
    We don’t wantto keep asking for pieces forever, which is the functionality we have currently. Instead, we want to end a game in the following circumstances: • (Part 1) When every cell is filled (i.e. after 9 moves). • (Part 2) When someone wins, with the following winning conditions: full column, full row, full forward diagonal or full backward diagonal ENDING A GAME: OVERVIEW 114
  • 115.
    V9.1 Add acounter to a game that counts the number of moves made, and is able to report that a game is finished when that counter exceeds 9. V9.2 A game should only continue while it is not finished. ENDING A GAME, PART 1: REQUIREMENTS 115
  • 116.
    ENDING A GAME,PART 1: REQUIREMENT V9.1 (1) 116 private int plays; private boolean finished; public boolean play(Piece piece, Move move) { if ( move.isValid() && canPlay(move) ) { board[move.getRow()][move.getColumn()] = piece; plays++; if ( plays == 9 ) finished = true; return true; } else { return false; } } Version 9, Game.java, play V9.1 Add a counter to a game that counts the number of moves made, and is able to report that a game is finished when that counter exceeds 9.
  • 117.
    V9.1 Add acounter to a game that counts the number of moves made, and is able to report that a game is finished when that counter exceeds 9. ENDING A GAME, PART 1: REQUIREMENT V9.1 (2) 117 public boolean gameOver() { return finished; } plays = 0; finished = false; Version 9, Game.java, gameOver and newGame
  • 118.
    while ( !game.gameOver()) { System.out.println("Next move for: " + piece); boolean valid = false; while (!valid) { int row = in.nextInt(); int column = in .nextInt(); Move move = new Move(row, column); valid = game.play(piece, move); } piece = new Piece(piece); } V9.2 A game should only continue while it is not finished. ENDING A GAME, PART 1: REQUIREMENT V9.2 118 Version 9, Main.java, main
  • 119.
    Version 10: EndingThe Game (Part 2) 119
  • 120.
    We don’t wantto keep asking for pieces forever, which is the functionality we have currently. Instead, we want to end a game in the following circumstances: • (Part 1) When every cell is filled (i.e. after 9 moves). • (Part 2) When someone wins, with the following winning conditions: full column, full row, full forward diagonal or full backward diagonal ENDING A GAME: OVERVIEW 120
  • 121.
    public boolean play(Piecepiece, Move move) { if ( move.isValid() && canPlay(move) ) { board[move.getRow()][move.getColumn()] = piece; plays++; checkWinner(); if ( plays == 9 ) finished = true; return true; } else { return false; } } ENDING A GAME, PART 2: SCAFFOLDING 121 public void checkWinner() { } We know that we want to check for a winner after each play. Methods provide us with the ability to scaffold for future functionality.Version 10, Game.java, play
  • 122.
    Let’s first imaginethat the only way to win a game of noughts and crosses is to fill the first column (column 0, as shown). We could add another method to our code to facilitate a check for this winning condition: ENDING A GAME, PART 2: ONE FULL COLUMN (AND MORE SCAFFOLDING) 122 public void checkWinner() { checkColumn(); } Version 10, Game.java, checkWinner and checkColumn public void checkColumn() { }
  • 123.
    V10.1 Complete thefunctionality in checkColumn to discern whether the first column contains a winning move by either player, and set a variable to indicate which player this is, should a winning move occur. V10.2 Change this method to determine whether any column contains a winning move by a player, and thus our notion of how a game is won. V10.3 When a game is over, if nobody has won (because all the moves have been exhausted), then print this, otherwise, print the winner. ENDING A GAME, PART 2: REQUIREMENTS 123
  • 124.
    ENDING A GAME,PART 2: REQUIREMENT V10.1 124 private Piece winner; public void checkColumn() { Piece extractedFirstPiece = board[0][0]; if ( extractedFirstPiece != null && extractedFirstPiece.matches(board[1][0]) && extractedFirstPiece.matches(board[2][0])) { finished = true; winner = extractedFirstPiece; } } V10.1 Write a method that checks whether the first column contains a winning move by either player, and sets a variable to indicate which player this is, should this occur. We use null here and short- circuit evaluation to protect against null references. 0 1 2 0 1 2 Version 10, Game.java, checkColumn
  • 125.
    🙈 ENDING A GAME,PART 2: REQUIREMENT V10.2 125 0 1 2 0 1 2 public void checkColumn(int column) { Piece extractedFirstPiece = board[0][column]; if ( extractedFirstPiece != null && extractedFirstPiece.matches(board[1][column]) && extractedFirstPiece.matches(board[2][column])) { finished = true; winner = extractedFirstPiece; } } public void checkWinner() { for ( int column = 0; column < 3; column++ ) { checkColumn(column); } } public Piece getResult() { return winner; } V10.2 Extend this method to determine whether any column contains a winning move by a player, and thus our notion of how a game is won. Version 10, Game.java, checkColumn, checkWinner and getResult
  • 126.
    ENDING A GAME,PART 2: REQUIREMENT V10.3 126 V10.3 When a game is over, if nobody has won (because all the moves have been exhausted), then print this, otherwise, print the winner. System.out.println( game + "n Game Over."); if ( game.getResult() == null ) { System.out.println("Nobody won :-("); } else { System.out.println(game.getResult() + " won :-)"); } Version 10, Main.java, main
  • 127.
  • 128.
    MAKING THINGS PRETTY 128 publicString toString() { String output = "+---+---+---+n"; for ( int row = 0; row < 3; row++ ) { output = output + "| "; for( int column = 0; column < 3; column++ ) { if ( board[row][column] == null ) { output = output + " | "; } else { output = output + board[row][column] + " | "; } } output = output + "n+---+---+---+n"; } return output; } Version 10, Game.java, toString
  • 129.
    PLAYING A GAME 129 +---+---+---+ || | | +---+---+---+ | | | | +---+---+---+ | | | | +---+---+---+ Next move for O: 0 0 +---+---+---+ | O | | | +---+---+---+ | | | | +---+---+---+ | | | | +---+---+---+ Next move for X: 0 2 +---+---+---+ | O | | X | +---+---+---+ | | | | +---+---+---+ | | | | +---+---+---+ Next move for O: 1 0 +---+---+---+ | O | | X | +---+---+---+ | O | | | +---+---+---+ | | | | +---+---+---+ Next move for X: 1 2 +---+---+---+ | O | | X | +---+---+---+ | O | | X | +---+---+---+ | | | | +---+---+---+ Next move for O: 2 0 +---+---+---+ | O | | X | +---+---+---+ | O | | X | +---+---+---+ | O | | | +---+---+---+ O won :-)
  • 130.
    🖥 The game showedin the previous slide is still only based on detecting whether either player completes an entire column. In the laboratory, complete the functionality, so that the remaining winning conditions can be detected: • A row is completed. • A forward diagonal is completed. • A backward diagonal is completed. You should add to the checkWinner method, and try and consider the efficiency of your solution: when do we no longer need to check for a winner? COMPLETING THE GAME LOGIC 130Topic7-2
  • 131.
    Dr. Martin Chapman programming@kcl.ac.uk martinchapman.co.uk/teaching ProgrammingPractice and Applications (4CCS1PPA) Topic 7: Arrays These slides will be available on KEATS, but will be subject to ongoing amendments. Therefore, please always download a new version of these slides when approaching an assessed piece of work, or when preparing for a written assessment. 131 Thursday 17th November