HI
Here is a truncated version (basically 1/3) of my program. Sorry it’s so long. It you care to take a look that would be awesome.
local json = require("json") function main() --------------------------------------------------------- --define FUNCTIONS --------------------------------------------------------- local loadQuestions\_and\_createGameDataFile local decodeJsonFileIntoContents local displaySplash ... [and a bunch more] ------------------------------------------------------ -- Define font name and sizes --------------------------------------------------------- MULTIPLE\_CHOICE\_FONT\_SIZE = 60 -- the text of the 3 answer choices LINK\_FONT\_SIZE = 60 -- for text link "next" and "intro" SCORES\_FONT\_SIZE = 55 -- for the scores on the score page ... [and more] ---------------------------- -- 1. FUNCTION loadQuestions\_and\_createGameDataFile -- load the database of questions from the json file in the resources directory into a table called "questions" -- create the file that will hold the game scores, if it's not already there ------------------------------------ loadQuestions\_and\_createGameDataFile = function () questions = json.decode( decodeJsonFileIntoContents( "table\_of\_questions.json" )) --\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* create the game\_data file in caches if it's not already there local path = system.pathForFile("game\_data.json",system.CachesDirectory) local file = io.open(path,"r") if file then --print("game\_data file exists") else -- it's not there so we must be playing for the first time local savePath = system.pathForFile("game\_data.json",system.CachesDirectory) local saveFile = io.open(savePath,"w") local data = { { question\_type="Who wrote what", high\_score=0, [... and more fields] }, { question\_type="Musical terms", high\_score=0, [... and more fields]}, { question\_type="Score excerpts", high\_score=0, [... and more fields] }, [...more] } local contents = json.encode(data) saveFile:write(contents) io.close(saveFile) end end --------------------------------------------------------- -- 2. FUNCTION decodeJsonFileIntoContents --------------------------------------------------------- decodeJsonFileIntoContents = function( filename) local path = system.pathForFile( filename, system.ResourcesDirectory ) local contents local file = io.open( path, "r" ) if file then contents = file:read( "\*a" ) io.close( file ) -- close the file after using it end return contents end -- end of function jasonFile --------------------------------------------------------- -- 3. FUNCTION displaySplash -- this is the intro splash to the app - only plays once. --------------------------------------------------------- displaySplash = function( ) displaySplashGroup=display.newGroup() local welcomeDisplay1 = display.newText(displaySplashGroup,"Music History Flashcards" , display.contentWidth \* 0.17,display.contentHeight \* 0.3, FONT\_NAME, 80 ) welcomeDisplay1.anchorX=0 welcomeDisplay1:setFillColor(0,0,0) displaySplashGroup.transitionfrom = transition.from( welcomeDisplay1, { time=1000, delay=0, alpha=0 } ) displaySplashGroup.transitionto = transition.to( welcomeDisplay1, { time=1000, delay=4000,alpha=0 } ) local welcomeDisplay2 = display.newText(displaySplashGroup,"Flashcards for the Baroque, Classical, Romantic and Modern Eras" , display.contentWidth \* 0.17, display.contentHeight \* 0.6, display.contentWidth \* .75,0, FONT\_NAME, 70 ) welcomeDisplay2.anchorX=0 welcomeDisplay2:setFillColor(.87,0,0) welcomeDisplay2.transitionfrom = transition.from( welcomeDisplay2, { time=1000, delay=0, alpha=0 } ) welcomeDisplay2.transitionto = transition.to( welcomeDisplay2, { time=1000, delay=4000, alpha=0 } ) ... [lots more text and image objects here] local startPlay = display.newText(displaySplashGroup,"Touch here to start" , display.contentWidth \* .5, display.contentHeight \* .5, FONT\_NAME,100 ) startPlay:setFillColor(0.8,0,0) startPlay.tranfrom = transition.from( startPlay, { time=1000, delay=27000, alpha=0 } ) startPlay:addEventListener("touch", skipIntroOrTouchHereToStartListener) end -- of displaySplash ----------------------------------- -- 12 FUNCTION: chooseQuestionType --this is the 'main menu' of the app where users can choose what type of question to answer ----------------------------------- chooseQuestionType = function() questionChoicesGroup = display.newGroup() local questionType\_repertoire = display.newText(questionChoicesGroup, "Representative works" , display.contentWidth \* .15,display.contentHeight \* .07, FONT\_NAME, MENU\_FONT\_SIZE) questionType\_repertoire.anchorX=0 questionType\_repertoire.anchorY=0 questionType\_repertoire:setFillColor(0,0,0) questionType\_repertoire.type = "repertoire" questionType\_repertoire:addEventListener("touch", chooseQuestionTypeListener) local questionType\_composer\_rep = display.newText(questionChoicesGroup, "Who wrote what" , display.contentWidth \* .15,display.contentHeight \* .19, FONT\_NAME, MENU\_FONT\_SIZE) questionType\_composer\_rep.anchorX=0 questionType\_composer\_rep.anchorY=0 questionType\_composer\_rep:setFillColor(0,0,0) questionType\_composer\_rep.type = "composer\_rep" questionType\_composer\_rep:addEventListener("touch", chooseQuestionTypeListener) ... [and a few more menu choices presented with text objects and listeners] local chooseType = display.newText(questionChoicesGroup, "Pick a topic" , display.contentWidth \* .63, display.contentHeight \* .7 , FONT\_NAME, MENU\_FONT\_SIZE ) chooseType:setFillColor(.87,0,0) end -- of chooseQuestionType ----------------------------------- -- 13. FUNCTION: chooseQuestionTypeListener -- uses global boolean variables to hold the user's choice of question type ----------------------------------- chooseQuestionTypeListener = function ( event ) if ( event.phase == "began" ) then display.getCurrentStage():setFocus(event.target) event.target:setFillColor(.87,0,0) elseif ( event.phase == "ended" ) then display.getCurrentStage():setFocus(nil) questionChoicesGroup:removeSelf() questionChoicesGroup = nil choice\_composer\_rep = false choice\_definitions = false choice\_excerpts = false choice\_bio = false choice\_composer\_style = false choice\_repertoire = false choice\_everything = false if (event.target.type == "composer\_rep") then choice\_composer\_rep = true elseif (event.target.type == "definitions") then choice\_definitions = true elseif (event.target.type == "excerpts") then choice\_excerpts = true ... [etc.] end initializeNewGame() displayCard() end end -- chooseQuestionTypeListener() ----------------------------------- -- 14. FUNCTION: initializeNewGame ----------------------------------- initializeNewGame = function() math.randomseed( os.time() ) questionsAnswered = 0 -- the number of questions answered questionsIgnored = 0 -- the number of question ignored (these are all the questions that aren't the type that the user chose to answer and need to be passed over when we iterate through the table of questions) score = 0 -- the total of correct answers inRegularCard = false -- are we answering a regular type question ... inExcerptCard = false -- ... or an excerpt card (which has a different layout)? -- new array which will start with as many elements as there are questions and hold values thus: array[1] = 1, array[2] = 2, etc. arrayToRandomizeQuestions = {} local rq for rq=1, #questions do arrayToRandomizeQuestions[rq] = rq end end -- end of function initializeNewGame() ----------------------------------- -- 12. FUNCTION: getRandomIndexForQuestions -- this generates a number at random from 1 to the number of questions remaining -- it then assigns this value to questionIndex and takes that number out of the array -- questionIndex is then used to pull the card at that position from the question table ----------------------------------- getRandomIndexForQuestions = function () local randomNumber = math.random(1, #questions - (questionsAnswered + questionsIgnored)) questionIndex = table.remove(arrayToRandomizeQuestions, randomNumber) end -- end of function getRandomIndexForQuestions() ------------------------------------------------ -- 13. FUNCTION displayCard -- this looks at what type of question the user wants to answer -- and calls the appropriate function (display regular card or display excerpt card) ------------------------------------------------ displayCard = function() getRandomIndexForQuestions() displayQuestionCardGroup = display.newGroup() if (choice\_composer\_rep == true) then if (questions[questionIndex].type == "composer\_rep" ) then displayRegularCard() else ignoreCard() end elseif (choice\_definitions == true) then if (questions[questionIndex].type == "definitions" ) then displayRegularCard() else ignoreCard() end elseif (choice\_excerpts == true) then if (questions[questionIndex].type == "excerpts") then displayExcerptCard() else ignoreCard() end ... [and a few more] end end -- end of function displayCard() ----------------------------------- -- 14. FUNCTION: displayRegularCard -- this displays the default type of flashcard with a text question and 3 answer choices ----------------------------------- displayRegularCard = function () number\_of\_guesses = 0 -- you get two guesses so we have to count this inRegularCard = true -- we are in the regular type card inExcerptCard = false -- display the question: local regularCardQuestion = display.newText(displayQuestionCardGroup, questions[questionIndex].question, display.contentWidth \* .1, display.contentHeight \* .33,display.contentWidth \* .88,0, FONT\_NAME, MULTIPLE\_CHOICE\_FONT\_SIZE ) regularCardQuestion.anchorX = 0 regularCardQuestion.anchorY = 0.5 regularCardQuestion:setFillColor(.87,0,0) local regularCardQuestionbackground = display.newRect( displayQuestionCardGroup, display.contentWidth \* 0.08,display.contentHeight \* .33,regularCardQuestion.width \* 1.05, regularCardQuestion.height \* 1.08 ) regularCardQuestionbackground.anchorX = 0 regularCardQuestionbackground.anchorY = 0.5 regularCardQuestionbackground.strokeWidth = 0 regularCardQuestionbackground:setFillColor( 1,1,1,.5) regularCardQuestionbackground:toBack() local x,y,z = get3Random() -- these 3 values are used to randomize the placement of the answer choices, so the right answer is not always in the same position local spacing = display.contentHeight \* .15 --display the 3 answer choices: local regularCardChoice1 = display.newText(displayQuestionCardGroup, questions[questionIndex].correct\_answer, display.contentWidth \* .1, display.contentHeight \* .35 + (x \* spacing), FONT\_NAME, MULTIPLE\_CHOICE\_FONT\_SIZE ) regularCardChoice1.anchorX = 0 regularCardChoice1.anchorY = 0 regularCardChoice1:setFillColor(0,0,0) regularCardChoice1.isCorrect = true regularCardChoice1:addEventListener( "touch", listenForResponseAndDisplayAnswerListener ) local regularCardChoice2 = display.newText(displayQuestionCardGroup,questions[questionIndex].wrong\_answer\_1, display.contentWidth \* .1, display.contentHeight \* .35 + (y \* spacing), FONT\_NAME, MULTIPLE\_CHOICE\_FONT\_SIZE ) regularCardChoice2.anchorX = 0 regularCardChoice2.anchorY = 0 regularCardChoice2:setFillColor(0,0,0) regularCardChoice2.isCorrect = false regularCardChoice2:addEventListener( "touch", listenForResponseAndDisplayAnswerListener ) local regularCardChoice3 = display.newText(displayQuestionCardGroup, questions[questionIndex].wrong\_answer\_2, display.contentWidth \* .1, display.contentHeight \* .35 + (z \* spacing), FONT\_NAME, MULTIPLE\_CHOICE\_FONT\_SIZE ) regularCardChoice3.anchorX = 0 regularCardChoice3.anchorY = 0 regularCardChoice3:setFillColor(0,0,0) regularCardChoice3.isCorrect = false regularCardChoice3:addEventListener( "touch", listenForResponseAndDisplayAnswerListener ) local which\_inquisitor = math.random(1,2) --RANDOMLY Pick one of two images if (which\_inquisitor == 1) then if (regularCardQuestionbackground.height \< display.contentHeight \* .25 ) then regularCardInquisitor = "pics/portraits/larger\_question\_card\_haydn.png" imageWidth = 189 --GLOBAL imageHeight = 180 --GLOBAL ... end -- display the randomly chosen image local inquisitor = display.newImageRect(displayQuestionCardGroup,regularCardInquisitor, imageWidth,imageHeight) inquisitor.anchorY = 1 inquisitor.x = display.contentWidth \* .16 inquisitor.y = (regularCardQuestionbackground.y ) - (regularCardQuestionbackground.height \* .5) inquisitor:toBack() ... end -- end of displayRegularCard() ----------------------------------- -- 15. FUNCTION: displayExcerptCard ----------------------------------- ... similar to the previous function but with different layout ----------------------------------- -- 16. FUNCTION: ignoreCard -- this function is necessary because we need to keep a count of -- how many cards are ignored ----------------------------------- ignoreCard = function () questionsIgnored = questionsIgnored + 1 if (questionsIgnored + questionsAnswered == #questions) then displayFinalScreen() else displayCard() end end -- ignoreCard() ----------------------------------- -- 17. FUNCTION: get3random ----------------------------------- get3Random = function () local a = math.random(1,3) local b = math.random(1,3) local c = math.random(1,3) while (a == b) do b = math.random(1,3) end while (c == b or c == a) do c = math.random(1,3) end return a,b,c end -- end of get3Random() ----------------------------------- -- 20. FUNCTION: listenForResponseAndDisplayAnswerListener ----------------------------------- listenForResponseAndDisplayAnswerListener = function ( event ) if ( event.phase == "began" ) then -- if 1 display.getCurrentStage():setFocus(event.target) event.target:setFillColor(.87, 0, 0) elseif ( event.phase == "ended" ) then display.getCurrentStage():setFocus(nil) if (event.target.isCorrect == false and number\_of\_guesses == 0) then -- the the user got it wrong the first time: give second chance: ... [prompt user to try again - uses text objects with transitions, all added to displayQuestionCardGroup] end else -- they either got it right first (or 2nd) time, or they've used up their 2nd chance displayQuestionCardGroup:removeSelf() displayQuestionCardGroup = nil displayAnswerCardGroup = display.newGroup() ... [increment the score if appropriate, increment the var holding the number of questions answered, and display the correct answer using various text/image objects] end -- end of if 2 end -- of if 1 end -- of function listenForResponseAndDisplayAnswerListener() ----------------------------------- -- 21. FUNCTION displayScoreInCorner ----------------------------------- ... ----------------------------------- -- 22. FUNCTION continueToNextCardOrQuit -- decide whether there are any cards left to display ----------------------------------- continueToNextCardOrQuit = function(event) if ( event.phase == "began" ) then display.getCurrentStage():setFocus(event.target) event.target:setFillColor(.87,0, 0,.3) elseif ( event.phase == "ended" ) then display.getCurrentStage():setFocus(nil) displayAnswerCardGroup:removeSelf() displayAnswerCardGroup = nil if ((questionsAnswered + questionsIgnored) \< #questions) then -- there are question left displayCard() else -- game is over - provide final score and feedback displayFinalScreen() end -- end of 2nd inner if end end -- end of continueToNextCardOrQuit() ----------------------------------- -- 23. FUNCTION: displayFinalScreen -- show the user their final score and provide links to see scores and return to menu -- also updates the score text file in the caches directory ----------------------------------- displayFinalScreen = function () local scoreAsPercentage = math.round(( score / questionsAnswered ) \* 1000) / 10 local gameDataTable = {} -- create the array/table that will hold game data finalScreenGroup = display.newGroup() ... [more text and image objects with listeners] local array\_index = 0 if choice\_composer\_rep == true then array\_index = 1 elseif choice\_definitions == true then array\_index = 2 elseif choice\_excerpts == true then array\_index = 3 elseif choice\_bio == true then array\_index = 4 elseif choice\_composer\_style == true then array\_index = 5 elseif choice\_repertoire == true then array\_index = 6 elseif choice\_everything == true then array\_index = 7 end local comment ... [assign value to 'comment' based on the score] gameDataTable = loadTable("game\_data.json") -- update the game data file: if (scoreAsPercentage \> gameDataTable[array\_index].high\_score) then gameDataTable[array\_index].high\_score = scoreAsPercentage end -- store the new high score if it is a new high gameDataTable[array\_index].cumulative\_score = gameDataTable[array\_index].cumulative\_score + scoreAsPercentage gameDataTable[array\_index].games\_played = gameDataTable[array\_index].games\_played + 1 gameDataTable[array\_index].average = math.round((gameDataTable[array\_index].cumulative\_score / gameDataTable[array\_index].games\_played)\*10)/10 saveTable(gameDataTable,"game\_data.json") -- write the array to the json file ... [display the results and the comment and links to see all scores and return to menu] end -- end of displayFinalScreen() ----------------------------------- -- 26. FUNCTION: playAgainListener ----------------------------------- playAgainListener = function ( event ) ... [called from the previous function - takes you back to the menu] end -- end of playAgainListener() ----------------------------------- -- 26.5 FUNCTION: seeAllScoresListener ----------------------------------- seeAllScoresListener = function ( event ) ... [deletes final screen group and calls the function that shows all the scores] end -- seeAllScoresListener() ----------------------------------- -- 26.6 FUNCTION: showAllScores -- presents a screen in tabular format displaying all the scores in all the question type categories ----------------------------------- showAllScores = function() showAllScoresGroup = display.newGroup() local gameDataTable = {} gameDataTable = loadTable("game\_data.json") localscoreHeading2 = display.newText(showAllScoresGroup, "high", display.contentWidth \* .6, 50, FONT\_NAME, MENU\_FONT\_SIZE ) scoreHeading2:setFillColor(0,0,0) local scoreHeading3 = display.newText(showAllScoresGroup, "#games", display.contentWidth \* .75, 50, FONT\_NAME, MENU\_FONT\_SIZE ) scoreHeading3:setFillColor(0,0,0) local scoreHeading4 = display.newText(showAllScoresGroup, "avg.", display.contentWidth \* .9, 50, FONT\_NAME, MENU\_FONT\_SIZE ) scoreHeading4:setFillColor(0,0,0) local line\_spacing = 0 local first\_line\_y = display.contentHeight \* .2 local arrayToHoldScores = {{},{},{},{},{},{},{}} for i=1, #gameDataTable - 1 do -- this has to be "#gameDataTable - 1" because the last array element doesn't have these fields arrayToHoldScores[i].question\_type = display.newText(gameDataTable[i].question\_type,display.contentWidth \* .1,first\_line\_y + line\_spacing,FONT\_NAME, SCORES\_FONT\_SIZE) arrayToHoldScores[i].question\_type.anchorX = 0 showAllScoresGroup:insert(arrayToHoldScores[i].question\_type) arrayToHoldScores[i].question\_type:setFillColor(.87,0,0) arrayToHoldScores[i].high\_score = display.newText( gameDataTable[i].high\_score ,display.contentWidth \* .6,first\_line\_y + line\_spacing,FONT\_NAME, SCORES\_FONT\_SIZE) showAllScoresGroup:insert(arrayToHoldScores[i].high\_score) arrayToHoldScores[i].high\_score:setFillColor(.87,0,0) [... etc.] line\_spacing = line\_spacing + (display.contentHeight \* .1) end local play\_again\_prompt\_background = display.newRect( showAllScoresGroup, display.contentWidth,display.contentHeight,display.contentWidth \* .37, display.contentHeight \* .27 ) play\_again\_prompt\_background.strokeWidth = 0 play\_again\_prompt\_background:setFillColor( 1,1,1,.5 ) play\_again\_prompt\_background:addEventListener("touch", playAgainAfterSeeingAllScoresListener) local play\_again\_prompt = display.newText(showAllScoresGroup, "menu", display.contentWidth \* .9, display.contentHeight \* .9, FONT\_NAME, MENU\_FONT\_SIZE) play\_again\_prompt:setFillColor(0,0,0) end -- end of showAllScores() ----------------------------------- -- 26.6 FUNCTION: playAgainAfterSeeingAllScoresListener ----------------------------------- playAgainAfterSeeingAllScoresListener = function ( event ) ... [go back to menu] end -- end of playAgainAfterSeeingAllScoresListener() ----------------------------------- -- 27. FUNCTION saveTable ----------------------------------- saveTable = function(t, filename) local path = system.pathForFile(filename,system.CachesDirectory) local file = io.open(path,"w") if file then local contents = json.encode(t) file:write(contents) io.close(file) return true else return false end end -- saveTable() ----------------------------------- -- 28. FUNCTION loadTable ----------------------------------- loadTable = function(filename) local path = system.pathForFile(filename, system.CachesDirectory) local contents = "" local gameDataTable = {} local file = io.open (path, "r") if file then local contents = file:read("\*a") gameDataTable = json.decode(contents) io.close(file) return gameDataTable end return nil end -- loadTable() ----------------------------------- -- END OF FUNCTION LISTING ----------------------------------- loadQuestions\_and\_createGameDataFile() -- this creates the timestamp file display.setStatusBar( display.HiddenStatusBar ) local splashBackground = display.newImageRect( "pics/score\_background.png",1425,900) splashBackground.anchorX=0 splashBackground.anchorY=0 displaySplash() end -- end of main function main()