Chapter 7: Accessing Files & Folders (Written Correctly?)

Did I write this correctly, if not where could I improve? Chapter 7 “accessing files & folders”.

use AppleScript version "2.4" -- Yosemite (10.10) or later
use scripting additions
--
-- Script Name: AppleScript Lottery Picker Game.scpt
-- Version: 1.0
-- Author: Jerry Lee Ford, Jr.
-- Date: August 2007
--
-- Description: This AppleScript game generates a list of lottery ticket
-- numbers and will print the list if instructed by
-- the player to do so.

-- Declare the following variable as having a global scope 
global GameTitle
-- Stores the list of lottery ticket numbers generated by the game 
set TicketList to ""
-- Tracks the numbers of tickets that have been generated 
set TicketCount to 0
-- Stores the game's name
set GameTitle to "AppleScript Lottery Picker"

-- Call handler that prompts the player to specify the range
-- of lottery numbers from which lottery tickets numbers should be
-- randomly selected
set Range to GetPlayerInput()

-- Call handler that prompts the player to specify how many numbers
-- it takes to make up one lottery ticket
set TicketSize to GetTicketSize()

-- Call handler that prompts the player to specify how many lottery
-- tickets are to be generated
set NoOfTickets to GetNoOfTickets()

-- This loop is responsible for managing the creation of the 
-- appropriate number of lottery, iterating once for each
-- ticket that is to be created
repeat NoOfTickets times
	-- Increment each time a new ticket is being generated
	set TicketCount to TicketCount + 1
	-- Call the handler responsible for generating individual
	-- lottery tickets
	set ticket to CreateTicket(Range, TicketSize)
	-- Format the display of individual lottery tickets
	if TicketCount < 10 then
		set ticket to TicketCount & ")" & ticket
		else
		set ticket to TicketCount & ")" & ticket
	end if
	-- Add the new number to the list of numbers generated by the game 
	set TicketList to TicketList & ticket & return & return
end repeat

-- Display the list of lottery ticket numbers that have been generated
set reply to button returned of (display dialog¬
"Here are your Lottery Ticket numbers:" & return & return¬
& TicketList buttons {"OK", "Print"} with title GameTitle)

-- Check to see if the player has elected to print a list of the 
-- lottery ticket numbers
if reply = "Print" then
	PrintLotteryTickets(TicketList)
end if

---------------------Handler Section------------------------
-- This handler prompts the player to tell the game the range of 
-- numbers from which lottery ticket numbers should be selected
on GetPlayerInput()
	-- This variable is used to control loop execution
	set ValidInput to false
	
	repeat until ValidInput = true -- Loop until input is collected 
		-- Prompt the player to specify the range of numbers to be used
		set NoRange to text returned of (display dialog¬
		"What is the highest number that can be selected when" & "created a lottery ticket?" default answer "44"¬
		buttons {"OK"} with title GameTitle)
		-- The range must be at least 3 and no larger than 59
		if (NoRange >2) and (NoRange < 60) then ¬
		set NoRange to NoRange as integer -- Convert the player input
			return NoRange -- Return the player's input
		else -- Display an error message if the input is not valid
			display dialog "Error: You must enter an integer value " &¬
			"between 3 and 59" with title GameTitle
		end if
	end repeat
end GetPlayerInput

-- This handler prompts the player to specify how many numbers it takes
-- to make up a lottery ticket
on GetTicketSize()
	-- Use a list to populate the listbox control from which the 
	-- player will specify how many numbers make up a lottery ticket
	set TicketSize to choose from list¬
	{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}¬
	with prompt "How many numbers do you need to select for each" &¬
	"lottery ticket?" with title GameTitle
	
	-- If the player clicked on the Cancel button instead of selecting a
	-- number, then set 6 as the default
	if TicketSize = false then set TicketSize to 6
	
	-- Coerce the player's input into an integer
	set TicketSize to TicketSize as integer
	return TicketSize -- Return the player's input
end GetTicketSize

-- This handler prompts the player to tell the game how many lottery
-- tickets the player plans on purchasing
on GetNoOfTickets()
	-- Use a list to populate the listbox from which the player will
	-- specify how many lottery tickets are to be purchased
	set NoTickets to choose from list¬
	{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}¬
	with prompt "How many lottery tickets are you going" &¬
	"to buy?" with title GameTitle
	
	-- If the player clicked on the Cancel button instead of selecting a 
	-- number, then set 1 as the default
	if NoTickets = false then set NoTickets to 1
	
	- Coerce the player's input into an integer
	set NoTickets to NoTickets as integer
	
	return NoTickets -- Renturn the player's input
end GetNoOfTickets

-- This handler is responsible for generating one complete lottery ticket
-- number set
on CreateTicket(Range, TicketSize)
	-- Define and initialize variables used within the handler
	set ValidTicket to false
	set NumberCount to 0
	set TempList to {}
	
	-- Loop until a valid ticket number set has been generated
	repeat until ValidTicket = true
		-- Retrieve a randomly generated number within the range
		-- specified by the player
		set randomNumber to GetRandomNumber(Range)
		
		-- Ensure that the number has not already been selected
		if randomNumber is not TempList then
			-- Pad single digit number with an extra blank space
			if randomNumber < 10 then¬
			set randomNumber to "" & randomNumber
			end if
			-- Add the random number to the list of numbers
					-- generated for the lottery ticket
					set TempList to TempList & randomNumber & ""
					
					-- Keep track of the number of valid lottery ticket numbers
					-- generated so far
					set NumberCount to NumberCount + 1
		end if
		-- Determine when a complete ticket's worth of numbers has
		-- been generated
		if NumberCount = TicketSize then
			set ValidTicket to true
		end if
	end repeat
	
	return TempList -- Return the completed list of lottery ticket numbers
end CreateTicket

-- This handler generates a random number representing a lottery
-- ticket number
on GetRandomNumber(Range)
	-- Generate a random number representing a lottery ticket
	set randomNo to random number from 1 to range
	return randomNo -- Return the random number to the calling statement
end GetRandomNumber

-- This handler uses the TextEdit application to print a list of the 
-- lottery ticket numbers generated by the game
on PrintLotteryTickets(TicketList)
	-- Save the lottery ticket as a temporary text file
	set LotteryFile to (path to temporary items folder as string) &¬
	"Lottery.txt"
	
	-- Open the file for writing
	set OutputFile to open for access file LotteryFile¬
	with write permission
	
	-- Overwrite any text already written to the file
	set eof of Output to 0
	
	-- Finish formatting text output
	set TicketList to¬
	"Lottery Ticket Numbers" & return & return & TicketList
	
	-- Write the lottery ticket to the file
	write TicketList to OutputFile
	
	-- Clost the text file
	close access OutputFile
	
	-- Use TextEdit to print the lottery ticket
	tell application "TextEdit"
		print LotteryFile
	end tell
	
	-- Notify the player that the list of lottery ticket numbers has been
	-- sent to the printer
	display dialog "Your lottery ticket numbers has been submitted" &¬
	"to the printer." buttons {"OK"} with title GameTitle
	
end PrintLotteryTickets

That should be:

set eof of OutputFile to 0

The ‘of’ is technically spurious too after ‘set eof’.

set eof OutputFile to 0

I also found two instances in the script of ‘if … then’ lines wrongly ending with line-continuation characters (in the GetPlayerInput() and CreateTicket() handlers) and a comment line beginning with one dash instead of two (in GetNoOfTickets()).

The two ‘use’ commands won’t have been in the script back in 2007.

Whichever version is the more correct version - for today - is the one I will go with. @NigelGarvey @ShaneStanley

‘improve’ is a wonderfully vague statement. one person’s ‘improvement’ may be irrelevant to someone else. That said, here are a few of my thoughts:

GetPlayerInput():

  set NoRange to text returned of (display dialog ¬
  	"What is the highest number that can be selected when" & "created a lottery ticket?" default answer ¬
  	"44" buttons {"OK"} with title GameTitle)

why the concatenation of the string when it can just be written as:

  set NoRange to text returned of (display dialog ¬
  	"What is the highest number that can be selected when creating a lottery ticket?" default answer ¬
  	"44" buttons {"OK"} with title GameTitle default button "OK")

Adding the ‘default button’ makes it easier to navigate via the keyboard.

GetTicketSize():

Personally, I don’t like the flow of this. If I click ‘Cancel’ it should cancel the entire script, not select some default (unknown) value.
I would write this handler as:

on GetTicketSize()
– Use a list to populate the listbox control from which the
– player will specify how many numbers make up a lottery ticket
set TicketSize to choose from list ¬
{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} ¬
with prompt ¬
“How many numbers do you need to select for each lottery ticket?” with title GameTitle ¬
default items {“6”}

-- If the player clicked on the Cancel button instead of selecting a
-- number, then bail
-- (note: 'tell me to quit' won't work fully when running inside SD, only as an app)
if TicketSize = false then tell me to quit

-- Coerce the player's input into an integer
set TicketSize to TicketSize as integer
return TicketSize -- Return the player's input

end GetTicketSize

GetNoOfTickets():

Same appplies here:

on GetNoOfTickets()
– Use a list to populate the listbox from which the player will
– specify how many lottery tickets are to be purchased
set NoTickets to choose from list ¬
{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} ¬
with prompt ¬
“How many lottery tickets are you going to buy?” with title GameTitle ¬
default items {“1”}
– If the player clicked on the Cancel button instead of selecting a
– number, then bail
– (note: ‘tell me to quit’ won’t work fully when running inside SD, only as an app)
if NoTickets = false then tell me to quit

-- Coerce the player's input into an integer
set NoTickets to NoTickets as integer
return NoTickets -- Renturn the player's input

end GetNoOfTickets

CreateTicket():

This line will not do what I think you want it to:

	if randomNumber is not TempList then

This will NEVER be true because randomNumber is an integer and TempList is a list, therefore this will always be false.
I think what you want is more like:

	if randomNumber is not in TempList then

which checks if the number is contained in the list.

Your pad code does not work:

		-- Pad single digit number with an extra blank space
		if randomNumber < 10 then
			set randomNumber to "" & randomNumber
		end if

This should be:

		-- Pad single digit number with an extra blank space
		if randomNumber < 10 then
			set randomNumber to "0" & randomNumber
		end if

why add an empty list item between each number?:

		set TempList to TempList & randomNumber & ""

At the very least I’d include a space:

		set TempList to TempList & randomNumber & " "

This changes the output from:


to:

but there are better ways of dynamically formatting a list.

Finally I’d add a default button to the final display dialog

set reply to button returned of (display dialog ¬
“Here are your Lottery Ticket numbers:” & return & return ¬
& TicketList buttons {“OK”, “Print”} with title GameTitle default button “OK”)

How are you posting your scripts?

Before the first line of the script on a line by itself you should have:

three “ticks” (to the left of the 1 on most QWERTY layouts) followed by the word “AppleScript”.

Your quoted script should follow. At the end, on a line by itself three more “ticks”

Hmmm… there seems to be an issue with display of AppleScripts

Maybe there’s a limit to the length?

Breaking it up into parts seems to help:

Also the “</>” tool seems to have changed its function. It used to allow you to post code and special characters. Now it just indents.



----why add an empty list item between each number?
-- To allow you to sort strings of numbers.
--using the & to concactenate strings results in a string.
--"X" & "Y" results in "XY"
set xyText to "X" & "Y"

-- but, {"X"} & "y" results in xyList, in fact using & with anything but text results in a list.

set xyList to {"X"} & "y"

set xyNumber to "X" & 3


--

--set TempList to TempList & randomNumber & ""
--At the very least I’d include a space:

--That would defeat the purpose (sorting numbers as text). 
--If you're making a list of random numbers between 1 and 100 as text, 
--and then sort them, you would get this order:

-- 21, 3, 35, 38, 42, 5, 67...
--
--with the 0 you get: 
--03, 05, 21,  35, 38, 42, 67...


--GetPlayerInput()
set gameTitle to "A Title for this Game"
set NoRange to text returned of (display dialog ¬
   "What is the highest number that can be selected when" & "created a lottery ticket?" default answer ¬
   "44" buttons {"OK"} with title gameTitle)
--why the concatenation of the string when it can just be written as:

set NoRange to text returned of (display dialog ¬
   "What is the highest number that can be selected when creating a lottery ticket?" default answer ¬
   "44" buttons {"OK"} with title gameTitle default button "OK")

----Adding the ‘default button’ makes it easier to navigate via the keyboard.
--This is true. You may also do that with Choose from list below

set NoRange to display dialog ("What is the highest number that can be selected when creating a lottery ticket?") ¬
   default answer ("44") ¬
   hidden answer false ¬
   buttons {"OK"} ¬
   default button 1 ¬
   with title gameTitle ¬
   giving up after 60


GetTicketSize(gameTitle)

--Personally, I don’t like the flow of this. If I click ‘Cancel’ it should cancel the entire script, not select some default (unknown) value.
--I would write this handler as:

on GetTicketSize(gameTitle)
   --– Use a list to populate the listbox control from which the
   --– player will specify how many numbers make up a lottery ticket
   set TicketSize to choose from list ¬
      {1, 2, 3, 4, 5, 6, 7, 8, 9, 10} ¬
         with prompt "How many numbers do you need to select for each lottery ticket? " with title gameTitle ¬
      default items {"6 "}
   
   set TicketSize to choose from list {1, 2, 3, 4, 5, 6, 7, 8, 9, 10} ¬
      with title gameTitle ¬
      with prompt ("How many numbers do you need to select for each lottery ticket? ") ¬
      default items 3 ¬
      OK button name ("Use These") ¬
      cancel button name ("Nevermind") ¬
      multiple selections allowed false ¬
      with empty selection allowed
   
   -- If the player clicked on the Cancel button instead of selecting a
   -- number, then bail
   -- (note: 'tell me to quit' won't work fully when running inside SD, only as an app)
   if TicketSize = false then tell me to quit
   
   -- Coerce the player's input into an integer
   set TicketSize to TicketSize as integer
   return TicketSize -- Return the player's input
   
end GetTicketSize

--FYI, the versions of Display Dialog and Choose From List that I added here come directly from Script Debuggers display of those commands in the Scripting Additions Dictionary.
--Just click on the command, and click paste tell. Use tab to toggle from one place holder to the next
on quit
	--continue quit --Note out when in Script Debugger/unnote when saved for use
end quit

I noticed that with my post - I assumed it was because I was posting snippets rather than entire scripts, but didn’t have time to debug.

1 Like

Are there any other changes I need to make to my script? Once I am done with my current applescript learning book, and made appropriate code corrections, I intend to make a resource thread to point new people to.

So this code correction isn’t just for me and my own uses. The resource thread I intend to create is a boilerplate “get started” reference you and other AppleScript pros can point new people to for the fundamentals.

Edit: There are 3 chapters left in the book I am using.

I am guessing there is nothing else I need to correct in my code? @estockly

Sorry, I haven’t gone through it. Post your latest version and I’ll have a look.