{"id":403,"date":"2018-03-27T18:09:06","date_gmt":"2018-03-27T18:09:06","guid":{"rendered":"http:\/\/loop.cs.mtu.edu\/?p=403"},"modified":"2023-11-06T17:28:22","modified_gmt":"2023-11-06T17:28:22","slug":"shamrock-treasure-hunt","status":"publish","type":"post","link":"https:\/\/loop.cs.mtu.edu\/index.php\/2018\/03\/27\/shamrock-treasure-hunt\/","title":{"rendered":"Shamrock Treasure Hunt"},"content":{"rendered":"<p><span style=\"font-weight: 400;\"><em><strong>Leapin\u2019 Leprechauns!<\/strong><\/em> \u00a0It\u2019s St. Patrick\u2019s Day and the leprechauns still can\u2019t find a safe place to hide their gold. \u00a0They think they\u2019ve gotten crafty and found a better place, but this game shows they might need to try again. \u00a0Similar to minesweeper, the Shamrock Treasure Hunt displays the distance to the leprechauns\u2019 gold when a tile is clicked.<\/span><!--more--><\/p>\n<p><span style=\"font-weight: 400;\">This program introduces several concepts including 2D arrays and GUI\u2019s.<\/span><\/p>\n<h1><b>Use of Arrays<\/b><\/h1>\n<p><span style=\"font-weight: 400;\">To keep track of the program logic, we will need to have a 2D array storing the location of the leprechaun\u2019s gold and the distance of each square to it. \u00a0A 2D array really just looks like a large grid like this, with values in this case representing the distance to the gold:<\/span><\/p>\n<table>\n<tbody>\n<tr>\n<td><span style=\"font-weight: 400;\">5<\/span><\/td>\n<td><span style=\"font-weight: 400;\">3<\/span><\/td>\n<td><span style=\"font-weight: 400;\">2<\/span><\/td>\n<td><span style=\"font-weight: 400;\">2<\/span><\/td>\n<td><span style=\"font-weight: 400;\">2<\/span><\/td>\n<td><span style=\"font-weight: 400;\">2<\/span><\/td>\n<\/tr>\n<tr>\n<td><span style=\"font-weight: 400;\">4<\/span><\/td>\n<td><span style=\"font-weight: 400;\">3<\/span><\/td>\n<td><span style=\"font-weight: 400;\">2<\/span><\/td>\n<td><span style=\"font-weight: 400;\">1<\/span><\/td>\n<td><span style=\"font-weight: 400;\">1<\/span><\/td>\n<td><span style=\"font-weight: 400;\">1<\/span><\/td>\n<\/tr>\n<tr>\n<td><span style=\"font-weight: 400;\">4<\/span><\/td>\n<td><span style=\"font-weight: 400;\">3<\/span><\/td>\n<td><span style=\"font-weight: 400;\">2<\/span><\/td>\n<td><span style=\"font-weight: 400;\">1<\/span><\/td>\n<td><span style=\"font-weight: 400;\">-1<\/span><\/td>\n<td><span style=\"font-weight: 400;\">1<\/span><\/td>\n<\/tr>\n<tr>\n<td><span style=\"font-weight: 400;\">4<\/span><\/td>\n<td><span style=\"font-weight: 400;\">3<\/span><\/td>\n<td><span style=\"font-weight: 400;\">2<\/span><\/td>\n<td><span style=\"font-weight: 400;\">1<\/span><\/td>\n<td><span style=\"font-weight: 400;\">1<\/span><\/td>\n<td><span style=\"font-weight: 400;\">1<\/span><\/td>\n<\/tr>\n<tr>\n<td><span style=\"font-weight: 400;\">5<\/span><\/td>\n<td><span style=\"font-weight: 400;\">3<\/span><\/td>\n<td><span style=\"font-weight: 400;\">2<\/span><\/td>\n<td><span style=\"font-weight: 400;\">2<\/span><\/td>\n<td><span style=\"font-weight: 400;\">2<\/span><\/td>\n<td><span style=\"font-weight: 400;\">2<\/span><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><span style=\"font-weight: 400;\">Wait a minute, what is -1 doing as the distance? \u00a0We are using -1 as a placeholder or sentinel value to mark the spot of the treasure. \u00a0We can\u2019t use 0 because Java automatically initializes the values of all array elements to zero unless each element\u2019s value is specified. \u00a0If we used 0, then the logic of some of our methods wouldn\u2019t work.<\/span><\/p>\n<p><span style=\"font-weight: 400;\"><strong>Dive deep!<\/strong>\u00a0 Click here to learn more about <a href=\"http:\/\/loop.cs.mtu.edu\/index.php\/2018\/04\/11\/sentinel-values\/\"><span style=\"color: blue;\">sentinel values<\/span><\/a>.<\/span><\/p>\n<p><span style=\"font-weight: 400;\"><strong>Important!<\/strong> \u00a02D arrays access elements as [row][column] where rows go across and columns go down. \u00a0This means that if you are storing x and y values for an image, you will access the array as array[y][x]. \u00a0Confusing these can result in lots of errors.<\/span><\/p>\n<h1><span style=\"font-weight: 400;\">LOGIC<\/span><\/h1>\n<h2><b>Setup &#8211; Getting the board ready<\/b><\/h2>\n<p><span style=\"font-weight: 400;\">Setup is pretty simple: \u00a0we call a method to hide the treasure and then call another helper method to fill the rest of the table.<\/span><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"null\">public void setup(Group g) {\r\n  hideTreasure();\r\n  fillTable(g);\r\n}<\/pre>\n<h2><b>Hide Treasure<\/b><\/h2>\n<p><span style=\"font-weight: 400;\">Quick! \u00a0Did you see the leprechaun disappear? \u00a0He\u2019s just magically hidden his treasure. \u00a0Well, not quite. We use random numbers to select where the treasure goes so it will be in a new spot each game.<\/span><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"null\">public void hideTreasure() {\r\n  treasX = (int) (Math.random() * cols);\r\n  treasY = (int) (Math.random() * rows);\r\n  System.out.println(\"x, y: \" + treasX + \", \" + treasY);\r\n  System.out.println(\"rows, cols: \" + rows + \", \" + cols);\r\n  board = new int[rows][cols];\r\n  board[treasY][treasX] = -1;\r\n}<\/pre>\n<p><span style=\"font-weight: 400;\">Math.random() returns a pseudo-random number between 0 and 1, based on the clock\u2019s internal computer. \u00a0We then multiply the result by the number of columns (or rows) to get a value between 0 and the number of columns. \u00a0Then we cast this back into an integer so we can use it as an index for the row and column.<\/span><\/p>\n<p><b>Dive deep!<\/b><span style=\"font-weight: 400;\"> \u00a0Learn more about random numbers.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">The two print statements aren\u2019t needed to make the program run, but I left them in to show a part of the debugging process used to make sure the program logic was running correctly. \u00a0If there is any doubt what value a variable has, a print statement with the variable name and value can really help!<\/span><\/p>\n<p><b>Dive deep!<\/b><span style=\"font-weight: 400;\"> \u00a0Learn more about <a href = \"http:\/\/loop.cs.mtu.edu\/index.php\/2018\/04\/11\/debugging-for-beginners\/\"><font color = \"blue\">debugging<\/font><\/a>.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Finally, \u00a0we initialize a new array and store it at the pointer provided by board.<\/span><\/p>\n<h2><b>Fill table<\/b><\/h2>\n<p><span style=\"font-weight: 400;\">How many steps do I need to get to the treasure? \u00a0Let\u2019s see\u2026 1, 2, 3, \u2026 <\/span><span style=\"font-weight: 400;\">Fortunately, there\u2019s a faster way to do the calculations.<\/span><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"null\">public void fillTable(Group g) {\r\n  for (int row = 0; row &lt; rows; row++) {\r\n    for (int col = 0; col &lt; cols; col++) {\r\n      if (board[row][col] != -1) {\r\n        board[row][col] = Math.max(Math.abs(row - treasY), Math.abs(col - treasX));\r\n      }\r\n      drawTile(col * width\/cols, row * height\/rows, width\/cols, -1, g);\r\n    }\r\n  }\t\r\n}<\/pre>\n<p><span style=\"font-weight: 400;\">The basic idea here is to iterate through all the elements in the array, calculating their distance from the treasure. By using a nested for-loop, we can automate the process of changing the row and column numbers, travelling through all the elements in the first row before visiting the second. \u00a0Inside the inner for-loop, we check to make sure the current position does not contain the treasure before calculating the distance. Because this game allows diagonal steps, we take the maximum of the absolute value of the distances from the current row to the treasure row and the current column to the treasure column. \u00a0Then we draw the tile with a placeholder value of -1 before continuing the for-loop.<\/span><\/p>\n<h1><b>GRAPHICS<\/b><\/h1>\n<h2><b>Group<\/b><\/h2>\n<p><span style=\"font-weight: 400;\">A group is a general all-purpose container to hold the shapes drawn. \u00a0Groups like g can be thought of a bit like a sheet of paper. You can put one group inside another like taping a paper to another paper. \u00a0You can also pass a group to multiple methods in a row like passing a paper down a lineup with each person drawing something on it before handing it to the next person. \u00a0<\/span><\/p>\n<h2><strong>Shapes<\/strong><\/h2>\n<p>Most of the ways of drawing things in JavaFX rely on shapes.\u00a0 A few shapes with their parameters and an example call are presented below:<\/p>\n<table>\n<tbody>\n<tr>\n<td><span style=\"font-weight: 400;\">Shape<\/span><\/td>\n<td><span style=\"font-weight: 400;\">Parameters<\/span><\/td>\n<td><span style=\"font-weight: 400;\">Example<\/span><\/td>\n<\/tr>\n<tr>\n<td><span style=\"font-weight: 400;\">Ellipse<\/span><\/td>\n<td><span style=\"font-weight: 400;\">centerX, centerY, radiusX, radiusY<\/span><\/td>\n<td><span style=\"font-weight: 400;\">new Ellipse(0, 0, 10, 10) <\/span><\/td>\n<\/tr>\n<tr>\n<td><span style=\"font-weight: 400;\">Rectangle<\/span><\/td>\n<td><span style=\"font-weight: 400;\">left-side, top, width, height<\/span><\/td>\n<td><span style=\"font-weight: 400;\">new Rectangle(0, 0, 30, 40)<\/span><\/td>\n<\/tr>\n<tr>\n<td><span style=\"font-weight: 400;\">Arc<\/span><\/td>\n<td><span style=\"font-weight: 400;\">centerX, centerY, radiusX, radiusY, startingAngle, degreesToRotate<\/span><\/td>\n<td><span style=\"font-weight: 400;\">new Arc(100, 100, 70, 50, 0, 180)<\/span><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><span style=\"font-weight: 400;\">You can learn more about the shapes provided in JavaFX at the javadocs website:<\/span><\/p>\n<p><a href=\"https:\/\/docs.oracle.com\/javase\/8\/javafx\/api\/javafx\/scene\/shape\/Shape.html\"><span style=\"font-weight: 400;\"><span style=\"color: blue;\">https:\/\/docs.oracle.com\/javase\/8\/javafx\/api\/javafx\/scene\/shape\/Shape.html<\/span><\/span><\/a><\/p>\n<h2><b>Draw Tile<\/b><\/h2>\n<p><span style=\"font-weight: 400;\">Here\u2019s the first of several methods devoted to graphics. \u00a0It\u2019s main purpose is drawing a tile for the game board.<\/span><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"null\">public void drawTile(double x, double y, double s, int dist, Group g) {\r\n  Rectangle r = new Rectangle(x, y, s, s);\r\n  r.setFill(Color.rgb(205, 235, 205));\r\n  r.setStroke(Color.BLACK);\r\n  g.getChildren().add(r);\r\n  drawShamrock(x + s * 0.1, y + s * 0.05, s * 0.75, s * 0.75, Color.rgb(0, 200, 0), g);\r\n  if (dist &gt; 0) {\r\n    Text text = new Text(x, y + 3 * s\/5 - 5, \"\" + dist);\r\n    text.setFill(Color.rgb(255,  195,  0));\r\n    text.setFont(new Font(s\/3));\r\n    if ( dist &lt; 10) {\r\n      text.setX(x + s\/2 - s\/9);\r\n    } else {\r\n      text.setX(x + s\/2 - s\/6);\r\n    }\r\n    g.getChildren().add(text);\r\n  }\r\n}<\/pre>\n<p><span style=\"font-weight: 400;\">The parameters x and y are the coordinates for the top left corner of the tile, and s is the length of the sides of the tile (our tiles are squares). \u00a0We make a point of using variables of type double when dealing with graphics because we will often want to represent parts of a pixel to keep the screen display from having random white or black pixels. \u00a0The integer dist is the value of the tile\u2019s distance (in tiles) from the treasure. Finally g is a general all-purpose container to hold the shapes drawn. Groups like g can be thought of a bit like a sheet of paper. \u00a0You can put one group inside another like taping a paper to another paper. You can also pass a group to multiple methods in a row like passing a paper down a lineup with each person drawing something on it before handing it to the next person. \u00a0<\/span><\/p>\n<p><span style=\"font-weight: 400;\">The first thing we do is draw a rectangle and set its fill color and its line color. Then we add it to the group and call a helper method to draw a shamrock. Then, if the distance is greater than 0, we draw text displaying the number of steps to the treasure. \u00a0If this distance is a single digit, we center it on the shamrock by finding the center(x + s \/ 2) and subtracting 1\/9 of the tile\u2019s width. If this distance is two digits, we find the center and subtract \u2159 of the width of the tile. \u00a0Then we add the text to the group as well.<\/span><\/p>\n<h2><b>Draw Shamrock<\/b><\/h2>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"null\">public void drawShamrock(double x, double y, double w, double h, Color c, Group g) {\r\n  Ellipse e1 = new Ellipse(x + w \/ 2, y + h \/ 4, w \/ 4, h \/ 4);\r\n  e1.setStroke(c);\r\n  e1.setFill(c);\r\n  Ellipse e2 = new Ellipse(x + w\/4, y + (h * 3\/5), w\/4, h\/4);\r\n  e2.setStroke(c);\r\n  e2.setFill(c);\r\n  Ellipse e3 = new Ellipse(x + 3 * w \/ 4, y + (h * 3\/5), w\/4, h\/4);\r\n  e3.setStroke(c);\r\n  e3.setFill(c);\r\n  Rectangle rect = new Rectangle(x + (w * 7\/16), y + h\/3, w\/8, h * 3\/5);\r\n  rect.setStroke(c);\r\n  rect.setFill(c);\r\n  g.getChildren().addAll(e1, e2, e3, rect);\r\n}<\/pre>\n<p><span style=\"font-weight: 400;\">The logic here is once again primarily geometry. \u00a0We take in parameters representing the x and y coordinates of the top left corner of the shamrock\u2019s bounding box as well as its width, height, and color. \u00a0We also have a parameter for the group of shapes to which the shamrock will be added. Then we draw the shamrock of three circles that each have a radius of \u00bc the height of the shamrock, and a rectangle centered in the middle as a stem. \u00a0For each of these shapes, we set the line color and fill color to be the same to create a uniform appearance. Finally, we add them to their group using the <em>g.getChildren().addAll()<\/em> command. This command lets us add any number of shapes to the list of shapes g already has: \u00a0the <em>getChildren()<\/em> is just another way of requesting the group\u2019s list of shapes. <\/span><\/p>\n<h2><b>End screen &#8211; Display the reward<\/b><\/h2>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"null\">public void displayEndScreen(Group g) {\r\n  drawRainbow2(width\/2, height\/4, width * 0.35, height * 0.35, g);\r\n  drawPotOGold(g);\r\n}<\/pre>\n<p><span style=\"font-weight: 400;\">This short little method makes another screen showing a pot \u2018o gold at the end of the rainbow when the player successfully completes the game. \u00a0\u00a0It calls two helper methods to accomplish its work.<\/span><\/p>\n<h2><b>Draw the Rainbow<\/b><\/h2>\n<p><span style=\"font-weight: 400;\">Scientists know rainbows are created by the diffraction of sunlight through raindrops, but we\u2019re making one from concentric arcs.<\/span><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"null\">public void drawRainbow(double x, double y, double w, double h, Group g) {\r\n  double wid = w \/ 12;\r\n  Arc red = new Arc(x + width\/2, y, w, h, 0, 180);\r\n  red.setFill(Color.rgb(255,  0,  0));\r\n  Arc orange = new Arc(x + width\/2, y, w - wid, h - wid, 0, 180);\r\n  orange.setFill(Color.rgb(255,  195,  0));\r\n  Arc yellow = new Arc(x + width\/2, y, w - (2 * wid), h - (2 * wid), 0, 180);\r\n  yellow.setFill(Color.rgb(255,  255,  0));\r\n  Arc green = new Arc(x + width\/2, y, w -  (3 * wid), h - (3 * wid), 0, 180);\r\n  green.setFill(Color.rgb(190,  255,  190));\r\n  Arc blue = new Arc(x + width\/2, y, w - (4 * wid), h - (4 * wid), 0, 180);\r\n  blue.setFill(Color.rgb(0,  190,  255));\r\n  Arc purple = new Arc(x + width\/2, y, w - (5 * wid), h - (5 * wid), 0, 180);\r\n  purple.setFill(Color.rgb(85,  85,  200));\r\n  Arc none = new Arc(x + width\/2, y, w - (6 * wid), h - (6 * wid), 0, 180);\r\n  none.setFill(Color.rgb(240,  240,  240));\r\n  g.getChildren().addAll(red, orange, yellow, green, blue, purple, none);\r\n}<\/pre>\n<p><span style=\"font-weight: 400;\">We take in the x and y coordinates for the center of the arc as well as the width and height we want our rainbow to be. \u00a0As usual, we also have a parameter for the group of shapes to which to add our new shape. The first thing we do is divide the width by 12 so we have a variable by which to decrement the width of the arc. \u00a0Then we draw each arc individually. The first two values taken as parameters by the arc function are the same for each arc because they represent the central point around which it rotates. Then we choose the width and height for the arcs by giving the radii for its associated ellipse, subtracting a linearly increasing amount each time we draw the arc so that the previous arcs will be visible. \u00a0The final two parameters are the starting angle and the number of degrees we will rotate through. Then we set the color of each arc. At the end, we all all the arcs to the group at once, drawing a rainbow that looks like this:<\/span><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-416\" src=\"http:\/\/loop.cs.mtu.edu\/wp-content\/uploads\/2018\/03\/Shamrock_rainbowPic1.jpg\" alt=\"\" width=\"1196\" height=\"547\" srcset=\"https:\/\/loop.cs.mtu.edu\/wp-content\/uploads\/2018\/03\/Shamrock_rainbowPic1.jpg 1196w, https:\/\/loop.cs.mtu.edu\/wp-content\/uploads\/2018\/03\/Shamrock_rainbowPic1-300x137.jpg 300w, https:\/\/loop.cs.mtu.edu\/wp-content\/uploads\/2018\/03\/Shamrock_rainbowPic1-768x351.jpg 768w, https:\/\/loop.cs.mtu.edu\/wp-content\/uploads\/2018\/03\/Shamrock_rainbowPic1-1024x468.jpg 1024w\" sizes=\"auto, (max-width: 706px) 89vw, (max-width: 767px) 82vw, 740px\" \/><\/p>\n<p><span style=\"font-weight: 400;\">You may be asking, why did we draw 7 arcs, but only see 6? \u00a0If you look closely, you will see a thin line along the bottom of the rainbow\u2019s center. \u00a0This is the bottom edge of the seventh arc which is set to be approximately the same color as the background. \u00a0You may have also noticed that there\u2019s a lot of repetition in the code above. This same image can be more efficiently created using a for-loop, but it is slightly harder to follow the order of the colors.<\/span><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"null\">public void drawRainbow2(double x, double y, double w, double h, Group g) {\r\n  double wid = w \/ 12;\r\n  Color [] rainbow = {\r\n      Color.rgb(255,  0,  0), Color.rgb(255,  195,  0), Color.rgb(255,  255,  0),\r\n      Color.rgb(190,  255,  190), Color.rgb(0,  190,  255), Color.rgb(85,  85,  200),\r\n      Color.rgb(240,  240,  240)\r\n  };\r\n  for (int i = 0; i &lt; 7; i++) {\r\n    Arc arc = new Arc(x + width\/2, y, w - (i * wid), h - (i * wid), 0, 180);\r\n    arc.setFill(rainbow[i]);\r\n    g.getChildren().add(arc);\r\n  }\r\n}<\/pre>\n<p><span style=\"font-weight: 400;\">Here we again calculate the width of each band of the rainbow. \u00a0Then we store all our rainbow colors in a color array. Then the loop draws the arcs from the outermost red band to the innermost gray one. \u00a0This overlays the inner arcs over the outer ones, creating the rainbow. When we draw each arc, we make it have a size of the full width minus the product of the width of each arc multiplied by the number of arcs we\u2019ve drawn. \u00a0After drawing the arc, we set its color using the index variable to get the right color out of the array. Then we add it to the group. Now we have a rainbow.<\/span><\/p>\n<h2><strong>Draw Pot &#8216;o Gold<\/strong><\/h2>\n<p>This is a simple method with straight-forward logic.\u00a0 We draw a rectangle for the body of the pot, an arc for the bottom, and an ellipse for the open top.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"null\">public void drawPotOGold(Group g) {\r\n  Rectangle rect = new Rectangle(1000, 300, 200, 200);\r\n  rect.setFill(Color.YELLOW);\r\n  rect.setStroke(Color.rgb(255, 205, 0));\r\n  Arc arc = new Arc(1100, 500, 100, 25, 180, 180);\r\n  arc.setFill(Color.YELLOW);\r\n  arc.setStroke(Color.rgb(255, 205, 0));\r\n  Ellipse ellipse = new Ellipse(1100, 300, 100, 50);\r\n  ellipse.setFill(Color.YELLOW);\r\n  ellipse.setStroke(Color.rgb(255, 205, 0));\r\n  Rectangle r = new Rectangle(1000, 700, 0, 0);\r\n  r.setFill(Color.rgb(245,  245,  245));\r\n  g.getChildren().addAll(rect, arc, ellipse, r);\r\n}<\/pre>\n<p>We make sure to set the fill color to be yellow for all three shapes.\u00a0 Then we set the stroke or line color to be not quite yellow.\u00a0 Why?\u00a0 Try setting the stroke to yellow to see.\u00a0 (Hint:\u00a0 with the stroke set to yellow, does the pot look 3D or flat?)<\/p>\n<h1><strong>GAME CONTROLS<\/strong><\/h1>\n<h2><b>Menus<\/b><\/h2>\n<p><span style=\"font-weight: 400;\"><em>Menus<\/em>: \u00a0things placed on restaurant tables to tell patrons what they can order. This is a slightly different kind of menu, but it serves a similar purpose: \u00a0giving users options in playing the game. If we\u2019re going to have a game that can be repeated or change level, we will want access to a menu to let users do these things.<\/span><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"null\">public MenuBar makeMenu(Stage stage) {\r\n  MenuBar bar = new MenuBar();\r\n  Menu game = new Menu(\"Game\");\r\n    \r\n  MenuItem newGame = newGameItem(stage);\r\n  MenuItem level = newLevelItem(stage);\r\n  MenuItem exit = newExitItem();\r\n\r\n  game.getItems().addAll(newGame, level, exit);\r\n  bar.getMenus().add(game);\r\n  return bar;\r\n}<\/pre>\n<p><span style=\"font-weight: 400;\">A menu has three main components: \u00a0a menu bar, which is that narrow strip across the top of many computer applications; one or more menus (subdirectories of options), and one or more menu items (each belonging to their own subdirectory). \u00a0This method creates only one menu, \u201cGame\u201d which then contains three items: new game, level, and exit. Because menu items are complex critters that need to be able to respond to user input, it is convenient to make helper methods that construct and return them. \u00a0Once all the menu items are made, they are added to their menu, which is then added to the menu bar. Finally, we return the menu bar. The single parameter variable is the window in which everything is displayed. Notice we pass it to two of the menu items. <\/span><\/p>\n<h2><b>Exit Item &#8211; Let\u2019s Get Outta Here!<\/b><\/h2>\n<p><span style=\"font-weight: 400;\">Your game is going badly and you feel like the leprechauns must have cast a spell on you to dull your wits. \u00a0Maybe if you end the game you can break the spell. You have two options. You can, of course, close the big red X button to close this like any other window, but you want something with more pizazz. \u00a0That\u2019s where the exit item comes in.<\/span><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"null\">private MenuItem newExitItem() {\r\n  MenuItem exit = new MenuItem(\"Exit\");\r\n  exit.setOnAction(new EventHandler&lt;ActionEvent&gt;() {\r\n\r\n    @Override\r\n    public void handle(ActionEvent event) {\r\n      System.exit(0);\r\n    }\r\n  });\r\n\r\n  return exit;\r\n}<\/pre>\n<p><span style=\"font-weight: 400;\">We create a new menu item, aptly named exit. \u00a0Then we need to attach an event handler to it so it responds to being clicked. \u00a0To do this, we use the setOnAction method which embeds the creation of a new EventHandler as its parameter. \u00a0This event handler is of type ActionEvent, basically meaning a mouse click with either button. We are then required to implement a method to tell the computer how to handle this action. \u00a0Here, we use <em>System.exit(0)\u00a0<\/em>to say \u201cEnd the whole program and let the computer know we exited in a normal state.\u201d Specifically, System.exit will shut down all currently running Java processes &#8211; fine for an application like this, but a <\/span><i><span style=\"font-weight: 400;\">really <\/span><\/i><span style=\"font-weight: 400;\">bad idea for homework assignments. \u00a0The 0 is used to indicate normal exiting, while other integers are used to designate error messages. \u00a0Finally we return the new menu item.<\/span><\/p>\n<h2><b>New Game &#8211; Start it up<\/b><\/h2>\n<p><span style=\"font-weight: 400;\">One game is okay, but what if you want multiple games? \u00a0A new game option lets you do just that.<\/span><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"null\">private MenuItem newGameItem(Stage stage) {\r\n  MenuItem newGame = new MenuItem(\"New Game\");\r\n  newGame.setOnAction(new EventHandler&lt;ActionEvent&gt;() {\r\n\r\n    @Override\r\n    public void handle(ActionEvent event) {\r\n      \/\/ create a new game\r\n      stage.setScene(newGame(stage));\r\n    }\r\n\r\n  });\r\n  return newGame;\r\n}<\/pre>\n<p><span style=\"font-weight: 400;\">This looks a lot the same as the exit item, except here we take in a parameter for the current window and handle the click differently. \u00a0We handle this event by resetting the scene for the whole window with a brand new one. We do this by calling the newGame method which initializes each game.<\/span><\/p>\n<h2><b>New Game &#8211; the logic<\/b><\/h2>\n<p><span style=\"font-weight: 400;\">We have to do a lot of prep to make a new game: \u00a0set all the variables, make a data representation of the board, and draw it.<\/span><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"null\">public Scene newGame(Stage stage) {\r\n  if (cols * height\/rows &lt; 3 * Screen.getPrimary().getBounds().getWidth()\/4) {\r\n    width = cols * height\/rows;\r\n  } else {\r\n    height = rows * width\/cols;\r\n  }\r\n  BorderPane big = new BorderPane();\r\n  Group group = new Group();\r\n  MenuBar bar = makeMenu(stage);\r\n  setup(group);\r\n  mouseInput(group, big);\r\n  big.setBottom(group);\r\n  big.setTop(bar);\r\n  return new Scene(big, width, height + 45);\r\n}<\/pre>\n<p><span style=\"font-weight: 400;\">The first thing we do is set the size of the window based on the number of columns and rows. \u00a0We create the constraint that we want the window to be no wider than \u00be of the screen. To get the screen width of the user\u2019s monitor, we use Screen.getPrinary().getBounds().getWidth(). \u00a0Then we set the window width to be the number of columns times the height \/ the number of rows. If the width is greater than \u00be of the screen, then we shrink the height so we can keep the tiles square at the same width. \u00a0<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Then we make a big pane to contain all the visible graphics in our window, including the game board and the menu bar. \u00a0We also make a group to pass to the helper methods for them to draw shapes into. We make our menu bar, setup our board, add mouse input, and place things where we want them. \u00a0Because <\/span><i><span style=\"font-weight: 400;\">big<\/span><\/i><span style=\"font-weight: 400;\"> is a BorderPane, it has five sections: \u00a0top, bottom, left, right, and center. Any part that doesn\u2019t contain another node has no size, so setting the menu bar to top, and group (our board) to bottom makes the board almost the whole size of the screen. \u00a0Then we create a scene holding our BorderPane and return it to the caller.<\/span><\/p>\n<h2><b>New Level Item &#8211; Time to take it to another level!<\/b><\/h2>\n<p><span style=\"font-weight: 400;\">We already have a game we can repeat, but a one-level game starts to get boring. \u00a0So let\u2019s give the user the option to set their level to any positive number of rows and columns.<\/span><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"null\">private MenuItem newLevelItem(Stage stage) {\r\n  MenuItem level = new MenuItem(\"Change Level\");\r\n  level.setOnAction(new EventHandler&lt;ActionEvent&gt;() {\r\n    @Override\r\n    public void handle(ActionEvent event) {\r\n      \/\/ Call the methods to change the level of the game\r\n      Stage chooseLevel = levelChooser(stage);\r\n      chooseLevel.show();\r\n    }\r\n  });\r\n  return level;\r\n}<\/pre>\n<p><span style=\"font-weight: 400;\">We start by making a menu item and setting the action when it is clicked. \u00a0In our case, we want a new window to open with text boxes to input the rows and columns. \u00a0To do this, we create a new stage with the helper method <\/span><i><span style=\"font-weight: 400;\">levelChooser<\/span><\/i><span style=\"font-weight: 400;\">, and tell the stage to show itself.<\/span><\/p>\n<h2><strong>Level Chooser &#8211; Pick Your Level<\/strong><\/h2>\n<p><span style=\"font-weight: 400;\"><em>Choices, choices.<\/em> \u00a0We\u2019re bored with finding the treasure too quickly so we want a harder level. \u00a0No problem. Level chooser lets you specify any number of rows and columns.<\/span><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"null\">private Stage levelChooser(Stage stage) {\r\n  Stage chooseLevel = new Stage();\r\n  TextField r = new TextField();\r\n  TextField c = new TextField();\r\n  Label ro = new Label(\"Rows: \");\r\n  Label co = new Label(\"Columns: \");\r\n  BorderPane bor = new BorderPane();\r\n  GridPane grid = new GridPane();\r\n  grid.add(ro, 0, 0);\r\n  grid.add(co, 0, 1);\r\n  grid.add(r, 1,  0);\r\n  grid.add(c, 1,  1);\r\n  Button ok = new Button(\"OK\");\r\n  ok.setOnAction(new EventHandler&lt;ActionEvent&gt; () {\r\n\r\n    @Override\r\n    public void handle(ActionEvent arg0) {\r\n      \/\/ Choose the level\r\n      chooseLevel(r, c);\r\n      chooseLevel.hide();\r\n        \r\n      \/\/ Update the window\r\n      Scene scene = newGame(stage); \t\/\/ Make a new game\r\n      stage.setScene(scene);\t\t\t\/\/ Set the scene\r\n      stage.sizeToScene();\t\t\t\/\/ Resize the window\r\n    }\r\n\r\n  });\r\n  bor.setTop(grid);\r\n  bor.setBottom(ok);\r\n  Scene s = new Scene(bor, 400, 400);\r\n  chooseLevel.setScene(s);\r\n  return chooseLevel;\r\n}<\/pre>\n<p><span style=\"font-weight: 400;\">This is a big chunk of code, so let\u2019s break it down into smaller bits. \u00a0The first 12 lines make a new window, create and label text fields, make a button, and organize it all in a grid pane. \u00a0A gridpane is a layout that puts things in tables. When adding a node to a gridpane, it is best to specify in which row and column you want it placed. \u00a0These are the parameters after the node, in the order row then column. <\/span><\/p>\n<p><span style=\"font-weight: 400;\">Once we\u2019ve make the button, we want to decide what happens when it is clicked. \u00a0Unlike with the main window, we don\u2019t want the program to close, so we decide to simply hide it. \u00a0First we call the helper method chooseLevel to take care of the logic of changing the number of rows and columns. \u00a0When we hide the level chooser, we call newGame(), assign the new game to be the scene, and resize it to fit the current board.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Finally, we put the button in the smaller window, set the top and bottom displays, set the scene to show our organized scene, and display our window.<\/span><\/p>\n<h2><b>Choosing a level &#8211; hidden helper<\/b><\/h2>\n<p><span style=\"font-weight: 400;\">This short little method could really be included in the big one above, but parsing out the contents of the text fields is important enough they seem to deserve their own method.<\/span><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"null\">private void chooseLevel(TextField r, TextField c) {\r\n  rows = Integer.parseInt(r.getText());\r\n  cols = Integer.parseInt(c.getText());\r\n}<\/pre>\n<p><span style=\"font-weight: 400;\">We take in two text fields representing rows and columns, and use the Integer method parseInt to get the int value from the string contained in each text field. \u00a0\u201cBut I entered numbers in the text fields!\u201d you say. Well, yes. However, the text field converts them into a string returned by its method getText(). While it\u2019s not implemented in this case, chooseLevel would be the perfect place to do input validation to ensure a user entered an integer greater than or equal to 1 in the text fields. \u00a0For now, we\u2019re just trusting the users to know what they\u2019re doing (and having our game freeze if they don\u2019t), but a real computer game should check to make it foolproof. If you\u2019re curious how to get error messages, try typing in words instead of numbers. You could also make it check for certain keywords like \u201ctwo\u201d if you want. \ud83d\ude42<\/span><\/p>\n<h2><b>Mouse Input <\/b><\/h2>\n<p><span style=\"font-weight: 400;\"><em>&#8220;Squeak! Squeak! \u00a0I\u2019d like more cheese, please.&#8221;<\/em>\u00a0 Not that sort of mouse &#8211; but a similar idea. \u00a0We want our mouse to tell the computer when we\u2019ve clicked a certain square because we want to know how close we are to the treasure.<\/span><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"null\">public void mouseInput(Group table, BorderPane window) {\r\n  table.setOnMouseClicked(new EventHandler&lt;MouseEvent&gt;() {\r\n\r\n    @Override\r\n    \/**\r\n     * Handle mouse-clicks on game tiles.\r\n     *\/\r\n    public void handle(MouseEvent event) {\r\n      double mouseX = event.getSceneX();\r\n      double mouseY = event.getSceneY() - 45;\r\n      System.out.println(mouseY);\r\n      \/\/ Determine which tile the mouse clicked\r\n      int r = (int)(mouseY\/(height\/rows)); \/\/ Calculate which row\r\n      int c = (int)(mouseX\/(width\/cols));  \/\/ Calculate which column\r\n      if (board[r][c] == -1) {\r\n        \/\/ The pot 'o gold is discovered\r\n        \/\/group1 = new Group();  \/\/ uncomment to remove double rainbow effect\r\n        displayEndScreen(group1);\r\n        window.setBottom(group1);\r\n      } else {\r\n        \/\/ A normal tile is clicked\r\n        drawTile(c * width\/cols, r * height\/rows, width\/cols, board[r][c], table);\r\n      }\r\n    }\r\n  });\r\n}<\/pre>\n<p><span style=\"font-weight: 400;\">We really just have a fancy \u201csetOnAction\u201d here like we encountered with buttons and menu items. \u00a0But it does some special jobs to handle the main mouse clicks of the game which occur on the board. \u00a0The first thing we do is determine where the mouse clicked in 2D coordinate space. Then we calculate which tile that correlates to. \u00a0Then, if the square in our array representing the board has a value of -1 (contains the treasure), we display the end screen at the bottom of the window. \u00a0Otherwise, we draw a tile with the number on it.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Note: \u00a0we currently have the line creating and assigning a new group to <em>group1<\/em> commented, resulting in the double rainbow effect below whenever the size of the board has gotten larger for the second game:<\/span><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-418\" src=\"http:\/\/loop.cs.mtu.edu\/wp-content\/uploads\/2018\/03\/Shamrock_doubleRainbow.jpg\" alt=\"\" width=\"1341\" height=\"500\" srcset=\"https:\/\/loop.cs.mtu.edu\/wp-content\/uploads\/2018\/03\/Shamrock_doubleRainbow.jpg 1341w, https:\/\/loop.cs.mtu.edu\/wp-content\/uploads\/2018\/03\/Shamrock_doubleRainbow-300x112.jpg 300w, https:\/\/loop.cs.mtu.edu\/wp-content\/uploads\/2018\/03\/Shamrock_doubleRainbow-768x286.jpg 768w, https:\/\/loop.cs.mtu.edu\/wp-content\/uploads\/2018\/03\/Shamrock_doubleRainbow-1024x382.jpg 1024w\" sizes=\"auto, (max-width: 706px) 89vw, (max-width: 767px) 82vw, 740px\" \/><\/p>\n<p><span style=\"font-weight: 400;\">What causes this? \u00a0Well, if we don\u2019t give the board a new group, it remembers the graphics of the old one, and simply adds more on, creating a graphic artifact. \u00a0Since a double rainbow looks twice as lucky, I decided to leave that line commented, but if you want to get rid of the glitch, just uncomment it.<\/span><\/p>\n<h1><strong>Pulling it all together<\/strong><\/h1>\n<h2><strong>Main<\/strong><\/h2>\n<p>Unlike normal non-graphic Java, the main method for a JavaFX app has only one command.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"null\">public static void main(String[] args) {\r\n  launch(args);\/\/ run the app\r\n}<\/pre>\n<p>launch(args) tells the program to get the start method running passing in any command-line arguments.<\/p>\n<h2><strong>Start<\/strong><\/h2>\n<p>Start does things like set up the screen, give it a scene, and make it visible.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"null\">public void start(Stage primaryStage) throws Exception {\r\n  Scene scene = newGame(primaryStage);\r\n  primaryStage.setScene(scene);\r\n  primaryStage.sizeToScene();\r\n  primaryStage.show();\r\n}<\/pre>\n<p>First, we create a new scene.\u00a0 then\u00a0 we set the stage scene to our new scene.\u00a0 Next we resize the stage or window to fit our scene.\u00a0 Finally, we display it.<\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"font-weight: 400;\">That\u2019s about it for making a simple game with JavaFX. \u00a0The complete code is below:<\/span><\/p>\n<p><a href=\"http:\/\/loop.cs.mtu.edu\/wp-content\/uploads\/2018\/03\/ShamrockTreasureHunt.txt\"><span style=\"color: blue;\">ShamrockTreasureHunt.txt<\/span><\/a><\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Leapin\u2019 Leprechauns! \u00a0It\u2019s St. Patrick\u2019s Day and the leprechauns still can\u2019t find a safe place to hide their gold. \u00a0They think they\u2019ve gotten crafty and found a better place, but this game shows they might need to try again. \u00a0Similar to minesweeper, the Shamrock Treasure Hunt displays the distance to the leprechauns\u2019 gold when a &hellip; <\/p>\n<p class=\"link-more\"><a href=\"https:\/\/loop.cs.mtu.edu\/index.php\/2018\/03\/27\/shamrock-treasure-hunt\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;Shamrock Treasure Hunt&#8221;<\/span><\/a><\/p>\n","protected":false},"author":4,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[10,31],"tags":[27,36,35,32,33,37],"class_list":["post-403","post","type-post","status-publish","format-standard","hentry","category-intermediate","category-java","tag-arrays","tag-cs1122","tag-cs1131","tag-gui","tag-javafx","tag-nested-for-loops"],"_links":{"self":[{"href":"https:\/\/loop.cs.mtu.edu\/index.php\/wp-json\/wp\/v2\/posts\/403","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/loop.cs.mtu.edu\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/loop.cs.mtu.edu\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/loop.cs.mtu.edu\/index.php\/wp-json\/wp\/v2\/users\/4"}],"replies":[{"embeddable":true,"href":"https:\/\/loop.cs.mtu.edu\/index.php\/wp-json\/wp\/v2\/comments?post=403"}],"version-history":[{"count":18,"href":"https:\/\/loop.cs.mtu.edu\/index.php\/wp-json\/wp\/v2\/posts\/403\/revisions"}],"predecessor-version":[{"id":462,"href":"https:\/\/loop.cs.mtu.edu\/index.php\/wp-json\/wp\/v2\/posts\/403\/revisions\/462"}],"wp:attachment":[{"href":"https:\/\/loop.cs.mtu.edu\/index.php\/wp-json\/wp\/v2\/media?parent=403"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/loop.cs.mtu.edu\/index.php\/wp-json\/wp\/v2\/categories?post=403"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/loop.cs.mtu.edu\/index.php\/wp-json\/wp\/v2\/tags?post=403"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}