Code technically works but

here is my code, its like playing BINGO, the result is, all the numbers used only ONCE.
there are 15 numbers or “BALLS”
No number is called twice
I only hit the return key 8 times instead of 15
cant figure it out

set bingo_numbers to {“B01”, “B02”, “B03”, “B04”, “B05”, “B06”, “B07”, “B08”, “B09”, “B10”, “B11”, “B12”, “B13”, “B14”, “B15”} as list
set called_numbers to {} as list
set count_of_called_numbers to 1

repeat until count_of_called_numbers is 15
set randomChoice to some item of bingo_numbers
if randomChoice is not in called_numbers then
display dialog randomChoice
else
repeat until randomChoice is not in called_numbers
set randomChoice to some item of bingo_numbers
end repeat
end if
set called_numbers to called_numbers & randomChoice
set count_of_called_numbers to count_of_called_numbers + 1
end repeat

called_numbers

Replace the curly quotes with straight quotes:

 set bingo_numbers to {"B01", "B02", "B03", "B04", "B05", "B06", "B07", "B08", "B09", "B10", "B11", "B12", "B13", "B14", "B15"}

Hi. Welcome to MacScripter.

Your dialog’s only being displayed when the first random choice in each repeat isn’t in called_numbers. When it is in that list, more attempts are made until a number’s found that isn’t in the list, but that’s after the dialog in your code. You should put the dialog later, when the when the choice for that repeat is finally decided. Oh. And start the counter at zero, otherwise you’ll only get fourteen numbers:

set bingo_numbers to {"B01", "B02", "B03", "B04", "B05", "B06", "B07", "B08", "B09", "B10", "B11", "B12", "B13", "B14", "B15"} as list
set called_numbers to {} as list
set count_of_called_numbers to 0

repeat until count_of_called_numbers is 15
	set randomChoice to some item of bingo_numbers
	repeat until randomChoice is not in called_numbers
		set randomChoice to some item of bingo_numbers
	end repeat
	display dialog randomChoice
	set called_numbers to called_numbers & randomChoice
	set count_of_called_numbers to count_of_called_numbers + 1
end repeat

called_numbers

By the way, it would help if you use this forum’s formatting tags when posting AppleScript code here. (Three backticks on separate lines above and below the code.) The code gets displayed with a button as above which allows people to open it automatically in their own editors. More formatting codes here.

Nigel, you gave me exactly what I was needing. Cant thank you enough. I would always try to use the INSERT CODE buttons but for some reason it did not work. I will try next time.

On a side note, after I get the list of numbers called in order, can I sort the list numerically, the variable numbers called.

Thank you so much. I tried Apple but it seems like the did away with AppleScript forum, I could not find it.

here is my final computer BINGO. All I need it to sort the final list of numbers called

I tried to use the right code format, Idont understand how to do it. Sorry, here is my code.

set bingo_numbers to {"B01", "B02", "B03", "B04", "B05", "B06", "B07", "B08", "B09", "B10", "B11", "B12", "B13", "B14", "B15", "I16", "I17", "I18", "I19", "I20", "I21", "I22", "I23", "I24", "I25", "I26", "I27", "I28", "I29", "I30", "N31", "N32", "N33", "N34", "N35", "N36", "N37", "N38", "N39", "N40", "N41", "N42", "N43", "N44", "N45", "G46", "G47", "G48", "G49", "G50", "G51", "G52", "G53", "G54", "G55", "G56", "G57", "G58", "G59", "G60", "O61", "O62", "O63", "O64", "OG5", "O66", "O67", "O68", "O69", "070", "071", "072", "073", "074", "075"} as list

set called_numbers to {} as list

set count_of_called_numbers to 0

repeat until count_of_called_numbers is 75
	
	set randomChoice to some item of bingo_numbers
	
	repeat until randomChoice is not in called_numbers
		
		set randomChoice to some item of bingo_numbers
		
		tell application "Finder" to set the clipboard to randomChoice as string
		
	end repeat
	
	display dialog (randomChoice) buttons {"END", "Continue"} default button "Continue"
	
	if button returned of the result is "End" then
		
		called_numbers
		
		exit repeat
		
	end if
	
	set called_numbers to called_numbers & randomChoice
	
	set count_of_called_numbers to count_of_called_numbers + 1
	
end repeat

called_numbers

Backtick tags added by NG.

I’ve done it for you this time. The trick is three backticks on separate lines above and below the script code:

```
AppleScript code here.
```

If all you want is a solution and don’t care how it’s derived, you might consider the following:

use framework "Foundation"
use framework "GameplayKit"
use scripting additions

set bingoNumbers to {"B01", "B02", "B03", "B04", "B05", "B06", "B07", "B08", "B09", "B10", "B11", "B12", "B13", "B14", "B15", "I16", "I17", "I18", "I19", "I20", "I21", "I22", "I23", "I24", "I25", "I26", "I27", "I28", "I29", "I30", "N31", "N32", "N33", "N34", "N35", "N36", "N37", "N38", "N39", "N40", "N41", "N42", "N43", "N44", "N45", "G46", "G47", "G48", "G49", "G50", "G51", "G52", "G53", "G54", "G55", "G56", "G57", "G58", "G59", "G60", "O61", "O62", "O63", "O64", "OG5", "O66", "O67", "O68", "O69", "O70", "O71", "O72", "O73", "O74", "O75"}
set randomSource to current application's GKARC4RandomSource's new()
set randomBingoNumbers to (randomSource's arrayByShufflingObjectsInArray:bingoNumbers)
set calledNumbers to current application's NSMutableArray's new()
repeat with aNumber in randomBingoNumbers
	(calledNumbers's addObject:aNumber)
	set buttonSelected to button returned of (display dialog (aNumber as text) buttons {"End", "Continue"} default button 2)
	if buttonSelected is "End" then exit repeat
end repeat
return (calledNumbers's sortedArrayUsingSelector:"localizedStandardCompare:") as list

If you want to stick with your existing script, add the following to the end of the script to sort:

set sortedNumbers to sortList(called_numbers)

on sortList(a)
	repeat with i from (count a) to 2 by -1
		repeat with j from 1 to i - 1
			if item j of a > item (j + 1) of a then
				set {item j of a, item (j + 1) of a} to {item (j + 1) of a, item j of a}
			end if
		end repeat
	end repeat
	return a
end sortList

Here is my version of the script. It doesn’t have a second repeat loop

use AppleScript version "2.4" -- Yosemite (10.10) or later
use scripting additions

set bingoNumbers to {"B01", "B02", "B03", "B04", "B05", "B06", "B07", "B08", "B09", "B10", "B11", "B12", "B13", "B14", "B15"}
set calledNumbers to {}

repeat with i from (count of bingoNumbers) to 1 by -1
	set n to random number from 1 to i
	set end of calledNumbers to item n of bingoNumbers
	display alert (last item of calledNumbers)
	set item n of bingoNumbers to item 1 of bingoNumbers
	set bingoNumbers to rest of bingoNumbers
end repeat

get calledNumbers
1 Like

Hi Robert.

:sunglasses: !

Just a minor optimisation. If you replace the chosen item with item i each time instead of item 1, you don’t need to keep making new source lists:

set bingoNumbers to {"B01", "B02", "B03", "B04", "B05", "B06", "B07", "B08", "B09", "B10", "B11", "B12", "B13", "B14", "B15"}
set calledNumbers to {}

repeat with i from (count bingoNumbers) to 1 by -1
	set n to random number from 1 to i
	set end of calledNumbers to item n of bingoNumbers
	display alert (result)
	set item n of bingoNumbers to item i of bingoNumbers
end repeat

get calledNumbers

Only your first script does what the OP asked for - randomly select 8 items out of 15. All other versions just sort the entire list.

Just change the repeat loop to be the number of iterations you want

As far as I can determine, the OP wants his script to work as follows:

  1. Prompt the user with a dialog containing a random bingo number.

  2. The user either selects Continue or End.

  3. If the user selects Continue, the random bingo number is added to the called numbers list. If the user selects Continue until all of the bingo numbers have been presented, the called numbers list is sorted and returned

  4. If the user selects End, the random bingo number in that dialog is not added to the called numbers list, which is then sorted and returned.

The OP does note in post 1 that his script stops after 8 iterations, but I think he considers that to be an issue that needs to be fixed. This is just a guess based on his following statement and on his second script which does not contain an 8 bingo-number limit.

I only hit the return key 8 times instead of 15
cant figure it out

Just my take on this. (edited to handle the edge case empty dialog at the end)

use AppleScript version "2.4" -- Yosemite (10.10) or later
use scripting additions

set bingoCallOptions to missing value

repeat
	set {thisBingoCall, bingoCallOptions} to chooseBingoCall(bingoCallOptions)
	if thisBingoCall is not "" then
		set bingoCallResults to button returned of (display dialog return & return & tab & tab & tab & tab & tab & tab & thisBingoCall & return & return buttons {"BINGO!", "Call Again"} default button "Call Again")
		if bingoCallResults is "BINGO!" or bingoCallOptions is {} then exit repeat
	else
		display dialog "All Bingo Calls have been used." buttons {"Ok"} default button "Ok"
		exit repeat
	end if
end repeat

on chooseBingoCall(bingoCallOptions)
	if bingoCallOptions is missing value then
		set bingoCallOptions to {"B - 1", "B - 2", "B - 3", "B - 4", "B - 5", "B - 6", "B - 7", "B - 8", "B - 9", "B - 10", "B - 11", "B - 12", "B - 13", "B - 14", "B - 15", "I - 16", "I - 17", "I - 18", "I - 19", "I - 20", "I - 21", "I - 22", "I - 23", "I - 24", "I - 25", "I - 26", "I - 27", "I - 28", "I - 29", "I - 30", "N - 31", "N - 32", "N - 33", "N - 34", "N - 35", "N - 36", "N - 37", "N - 38", "N - 39", "N - 40", "N - 41", "N - 42", "N - 43", "N - 44", "N - 45", "G - 46", "G - 47", "G - 48", "G - 49", "G - 50", "G - 51", "G - 52", "G - 53", "G - 54", "G - 55", "G - 56", "G - 57", "G - 58", "G - 59", "G - 60", "O - 61", "O - 62", "O - 63", "O - 64", "O - 65", "O - 66", "O - 67", "O - 68", "O - 69", "O - 70", "O - 71", "O - 72", "O - 73", "O - 74", "O - 75"}
	end if
	set tids to AppleScript's text item delimiters
	set thisBingoCall to some item of bingoCallOptions
	set AppleScript's text item delimiters to "•"
	set bingoCallOptions to ("•" & bingoCallOptions as text) & "•"
	set AppleScript's text item delimiters to "•" & thisBingoCall & "•"
	set bingoCallOptions to text items of bingoCallOptions
	set AppleScript's text item delimiters to "•"
	set bingoCallOptions to bingoCallOptions as text
	set bingoCallOptions to text items 2 thru -2 of bingoCallOptions
	set AppleScript's text item delimiters to tids
	return {thisBingoCall, bingoCallOptions}
end chooseBingoCall

Your version will return 8 random items from the first 8 items in this case

Here’s another take, which is a bit slower, but shows how to do it as if you were actually pulling bingo numbers from a bin, or dealing cards from a deck.

As each number is pulled, it is removed from the source.

set bingo_numbers to {"B01", "B02", "B03", "B04", "B05", "B06", "B07", "B08", "B09", "B10", "B11", "B12", "B13", "B14", "B15"} as list
set the desiredNumber to 15
set called_numbers to {}
set count_of_bingo_numbers to count of bingo_numbers

repeat desiredNumber times
	set x to random number from 1 to count_of_bingo_numbers
	set the end of called_numbers to item x of bingo_numbers
	if x = 1 then
		set bingo_numbers to the rest of bingo_numbers
	else if x = count_of_bingo_numbers then
		set bingo_numbers to items 1 thru (count_of_bingo_numbers - 1) of bingo_numbers
	else
		set bingo_numbers to (items 1 thru (x - 1) of bingo_numbers) & (items (x + 1) thru count_of_bingo_numbers of bingo_numbers)
	end if
	set count_of_bingo_numbers to count_of_bingo_numbers - 1
end repeat

called_numbers
set bingoNumbers to {"B01", "B02", "B03", "B04", "B05", "B06", "B07", "B08", "B09", "B10", "B11", "B12", "B13", "B14", "B15"}
set calledNumbers to {}

set sourceCount to (count bingoNumbers)
set numberRequired to 8
repeat with i from sourceCount to (sourceCount - numberRequired + 1) by -1
	set n to random number from 1 to i
	set end of calledNumbers to item n of bingoNumbers
	display alert (result)
	set item n of bingoNumbers to item i of bingoNumbers
end repeat

get calledNumbers

Nigel. I was just curious how you would comply with the OP’s request to sort calledNumbers? My bubble sort is slow but with 8 or so numbers perhaps this doesn’t matter.

Hi peavine.

Yeah. When there’s only a few items to sort, it makes practically no difference what kind of sort’s used. In fact simple sorts like bubble or insertion ought to be very slightly faster, as they don’t have the overhead of other sorts’ “heavy lifting” formalities.

Even a bubble sort can be optimised up to a point. While its formal description says that adjacent items are swapped if they’re in the wrong order, its also the case one of the items will be taking part in the following comparison too, so it makes sense to hang on to it rather than put it back into the list and then fetch it out again immediately. This is what happens my own version of bubble sort below. Like yours, it sorts the input list in place, so it deliberately doesn’t return a result in order not to give the impression it’s producing a sorted copy of the list. It also has some smart-arse stuff at the beginning which allows the sort range to be specified using positive or negative indices:

on bubbleSort(theList, l, r) -- Sort items l thru r of theList.
	set listLen to (count theList)
	if (listLen < 2) then return
	-- Convert negative and/or transposed range indices.
	if (l < 0) then set l to listLen + l + 1
	if (r < 0) then set r to listLen + r + 1
	if (l > r) then set {l, r} to {r, l}
	
	script o
		property lst : theList
	end script
	
	set lPlus1 to l + 1
	repeat with j from r to lPlus1 by -1
		set lv to o's lst's item l
		-- Hereafter lv is only set when necessary and from rv rather than from the list.
		repeat with i from lPlus1 to j
			set rv to o's lst's item i
			if (lv > rv) then
				set o's lst's item (i - 1) to rv
				set o's lst's item i to lv
			else
				set lv to rv
			end if
		end repeat
	end repeat
	
	return -- nothing.
end bubbleSort

bubbleSort(called_numbers, 1, -1) -- Sort items 1 thru -1 of called_numbers.
return called_numbers

Similar retentive behaviour can be applied in an insertion sort:

on insertionSort(theList, l, r) -- Sort items l thru r of theList.
	set listLength to (count theList)
	if (listLength < 2) then return
	if (l < 0) then set l to listLength + l + 1
	if (r < 0) then set r to listLength + r + 1
	if (l > r) then set {l, r} to {r, l}
	
	script o
		property lst : theList
	end script
	
	set highestSoFar to o's lst's item l
	set rv to o's lst's item (l + 1)
	if (highestSoFar > rv) then
		set o's lst's item l to rv
	else
		set highestSoFar to rv
	end if
	repeat with j from (l + 2) to r
		set rv to o's lst's item j
		if (highestSoFar > rv) then
			repeat with i from (j - 2) to l by -1
				set lv to o's lst's item i
				if (lv > rv) then
					set o's lst's item (i + 1) to lv
				else
					set i to i + 1
					exit repeat
				end if
			end repeat
			set o's lst's item i to rv
		else
			set o's lst's item (j - 1) to highestSoFar
			set highestSoFar to rv
		end if
	end repeat
	set o's lst's item r to highestSoFar
	
	return -- nothing.
end insertionSort

insertionSort(called_numbers, 1, -1) -- Sort items 1 thru -1 of called_numbers.
return called_numbers

This is a fairly common scenario - randomly selecting from a list, ensuring that no item is repeated.

Your approach is the obvious one, and perfectly valid, but there is a (arguably better) way of doing it.

The problem with the ‘select an item and see if it’s already been used’ is that it gets exponentially harder as you approach the end of the list - if you have a list of 100 items, then there’s a 99% chance that the 100th iteration is going to match an already-selected number, meaning you spin a lot of cycles selecting items from the list before you get a unique.

The alternative is to just take the list you have and ‘shuffle’ it. This avoids all chance of duplicates, or any need to check whether a number has already been used.

set bingo_numbers to {"B01", "B02", "B03", "B04", "B05", "B06", "B07", "B08", "B09", "B10", "B11", "B12", "B13", "B14", "B15"} as list

set rand_index to 100 -- how 'random' to make the list - higher number = more randomization
set item_count to count bingo_numbers
repeat rand_index times
	set i to random number from 1 to item_count
	set j to random number from 1 to item_count
	set {item i of bingo_numbers, item j of bingo_numbers} to {item j of bingo_numbers, item i of bingo_numbers}
end repeat

Now you have a pre-randomized list of numbers that is guaranteed to contain no duplicates. From here it’s easy to walk the list:

repeat with i from 1 to item_count
	display dialog (item i of bingo_numbers)
end repeat

Thanks Nigel. I put these sort alternatives to the test with a timing script. My test list contained 10 and 1000 items and the results were:

Sort Method - 10 items - 1000 items
Simple bubble sort - 1 millisecond - 23.7 seconds
Enhanced bubble sort - 1 millisecond - 442 milliseconds
Insertion sort - 0 millisecond - 224 milliseconds

As you suggest, the simple bubble sort is very usable with a small number of items:

Test List Contains - Result
10 items - 1 millisecond
50 items - 13 milliseconds
100 items - 55 milliseconds
200 items - 247 milliseconds

The test computer was a 2023 Mac mini.