AppleScript to make variables for AppleScript

I’m playing around trying to make a card game. The script asks for how many players there are going to be. The number (between 2 and 6) is returned and set to a variable. Now I want to set a list with sub lists such as:

set theHands to {h1:{},h2:{},h3:{}}

What I’d like to do is set the h and the number (h1, h2, etc) to however many players have been designated. So, is it possible to use a loop or some such to create a variable name for each of the sublists?

Thanks.

PreTech

I don’t understand why you don’t just fill theHands with all possible (6) hands set to {}, but then just iterate your game through the first 3, if 3 was the number of players, but don’t make them a record, make them a list of lists so you can refer to them as item n of theHands.

Adam,

That idea occured to me (which is probably what I’ll do) but I was curious if this was actually possible.

PreTech

Pretech;

There are several threads about this and while I can’t remember the details, the effect is this: you can’t compute the name of a record (get it from a variable) without a scripting hack that relies on trapping an error message in a try block, parsing it, and running it as a script.

Adam,

Thanks. I thought I had seen something about this somewhere a while back but I could not seem to find it. I’ll try searching again.

PreTech

As a rule, whenever you find yourself trying to programmatically create the names of variables, your approach is the wrong one.

You have a number returned that is the number of players.

Use that number to loop, and in each iteration of the loop add a list to the end of the main list.
That way you wind up with a list of N lists, where N= the number that was typed in.

set theMainList to {}
set theSublist to {}
set N to text returned of (display dialog "enter a number" default answer 0)
repeat N times
	set theMainList to theMainList & {theSublist}
end repeat

Produces ==> {{}, {}, {}, {}, {}, {}}

johnnylundy,

That was basically what I was trying to do except I wanted to name each list in the main list (make a record) for easier referencing. But, then again, refering to them as item x of mainList is not that hard.

Thanks.

PreTech

You can’t set a record item value if it doesn’t already exist. You must create a list (as in the ‘resetHands()’ routine below) before you can begin setting it’s values. It’s probably just best to pre-declare a list of empty pairs large enough to handle your maximum number of players… although a routine like the “initHands()” routine below can create a list dynamically with only the number of variables you need…

property theHands : {}

(* Use these instead of 'resetHands()' to create a dynamic list of players *)
--property numberOfPlayers : 10
--initHands()

resetHands()
setHand(3, {1, 2, 3, 4, 5})
log (getHand(3))

return theHands

to setHand(theHand, theCards)
	set theHands to (run script "on run{theHand, theCards, theHands}
	set h" & theHand & " of theHands to theCards
	return theHands
end" with parameters {theHand, theCards, theHands})
end setHand

to getHand(theHand)
	run script "on run{theHand, theHands}
	return h" & theHand & " of theHands
end" with parameters {theHand, theHands}
end getHand

to resetHands()
	set theHands to {h1:{}, h2:{}, h3:{}, h4:{}, h5:{}, h6:{}, h7:{}, h8:{}, h9:{}, h10:{}}
end resetHands

to initHands()
	set tmpScript to "{"
	
	repeat with tmpIndex from 1 to numberOfPlayers
		set tmpScript to (tmpScript & "h" & tmpIndex & ":{}")
		if tmpIndex < numberOfPlayers then set tmpScript to (tmpScript & ",")
	end repeat
	
	set tmpScript to (tmpScript & "}")
	
	set theHands to (run script "on run{tmpScript}
	return " & tmpScript & "
end" with parameters {tmpScript})
	
	return theHands
end initHands

j

Sorry guys - I intended to come back on this one earlier, but managed to get diverted (again). :slight_smile:

I might as still well throw in my own 2¢ on this (although my conclusion pretty much echoes what’s been said). Exploring the pros and cons of different techniques can be a useful process, since it often helps to convince us about the best way forward in a given situation (rather than merely accepting a particular method as the “best” way or “a really bad idea”. When embarking on a project, it’s also useful to outline the entire process before committing to a general approach. Consideration of what may be required later in a script can help to avoid the frustration of painting oneself into a corner.

As we’ve seen, it’s a perfectly possible to actually script the creation of records based on a predetermined labelling scheme. Here’s another variation:

to |deal the cards| from d to p
	set t to ""
	set tid to text item delimiters
	set text item delimiters to "\", \""
	tell (count d) div p to repeat with i from it to it * p by it
		set t to t & ", player_" & i div it & ":{\"" & d's items (i - it + 1) thru i & "\"}"
	end repeat
	set text item delimiters to tid
	run script "{" & t's text 3 thru end & "}"
end |deal the cards|

set |top of the deck| to {"five", "Queen", "Ace", "eight", "two", "King"}
set |the players| to choose from list {1, 2, 3, 4, 5, 6} with prompt "How many players?"
if |the players| is false then return

|deal the cards| to |the players| from the |top of the deck|

The difficulty arises in accessing a record, mainly because its labels are not defined at compile time - so the script won’t really know in advance what they are - or, consequently, how to refer to them. You’d need to come up with some referencing method (which will probably be index-based, anyway). Given consistent labelling and the ability to count items, a referencing of sorts can be achieved - which is where a few tricks, including the so-called “hack” to which Adam referred earlier, might be utilised:

on |quoted text| from v
	try
		{v}'s v
	on error v
		set d to text item delimiters
		set text item delimiters to "{"
		set v to v's text from text item 2 to end
		set text item delimiters to "}"
		set v to v's text beginning thru text item -2
		set text item delimiters to d
		v
	end try
end |quoted text|

set |hands dealt| to {player_2:{"Ace", "eight"}, player_1:{"five", "Queen"}, player_3:{"two", "King"}}
set player_number to 2
run script (|quoted text| from |hands dealt|) & "'s player_" & player_number

--> {"Ace", "eight"}

Note that the variable |hands dealt|, shown above, is a record in which the labels don’t appear in sequence - and this is where such extraction methods can be helpful in obtaining the correct values. However, dynamic labelling almost invariably produces records with labels in a predetermined sequence. If you’re sure that the original sequence will remain intact, then the extraction of values can be greatly simplified:

set |hands dealt| to {player_1:{"five", "Queen"}, player_2:{"Ace", "eight"}, player_3:{"two", "King"}}
item 2 of (|hands dealt| as list)

--> {"Ace", "eight"}

But then again, if values are to be obtained by index, it’s usually much cleaner to just use a list directly:

set |hands dealt| to {{"five", "Queen"}, {"Ace", "eight"}, {"two", "King"}}
item 2 of |hands dealt|

--> {"Ace", "eight"}

So, while records can be very useful in certain situations, I’m not entirely convinced that this would be one of them. (I’m obviously viewing this fairly superficially - and the devil may well be in your detail…)

I just dug out an old card game routine that I cobbled together a while back, which is based entirely on list manipulation. In case it might help, I’ve included part of it here - up to the point where the hands are dealt and sorted. (Even this truncated version is a bit lengthy, but the game play sections of the original were just too long to include here.)

The script attempts to emulate pretty much what happens in a regular card game, using only vanilla AppleScript. A fresh deck is taken and shuffled. A user-defined amount of cards is then dealt to a number of players (also user-defined). Each hand is then sorted by value (2, 3, 4, 5, 6, 7, 8, 9, 10, J, Q, K, A) within each suit (hearts, clubs, diamonds, spades). This “shortened” version ends by simply displaying the cards in each hand. (And not a record in sight…)

script get_deck
	set d to {}
	repeat with s in «data utxt2661266326622660» as Unicode text
		repeat with c in {"2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A"}
			set d's end to c & s
		end repeat
	end repeat
	d
end script

script get_index
	tell {}
		repeat with i from 1 to 52
			set end to i
		end repeat
		it
	end tell
end script

property full_deck : run get_deck
property index_list : run get_index
property every_player : 4
property each_hand : 13
property empty_hands : {}
property variable_hands : false
property hand_variance : ""

to shuffle_(current_deck)
	script o
		property d : current_deck
		property l : my index_list's items
	end script
	repeat with i from 1 to 52
		tell some number of o's l
			set o's l's item it to false
			set o's d's item i to my full_deck's item it
		end tell
	end repeat
end shuffle_

to count_(p)
	repeat
		tell (display dialog "How many players?" default answer p) to try
			tell text returned as integer to if it > 0 and it < 53 then return it
		end try
	end repeat
end count_

to count_cards of h for p
	set l to {}
	set m to 52 div p
	repeat with n from 1 to m
		set l's end to n
	end repeat
	if m < h then set h to m
	if 52 mod p > 0 then tell "Deal all cards"
		set l's end to it
		if variable_hands then set h to it
	end tell
	tell (choose from list l with prompt "How many cards for each player?" default items h)
		if it is false then error number -128
		set variable_hands to it contains "Deal all cards"
		if variable_hands then tell 52
			set each_hand to it div p
			set hand_variance to "/" & each_hand + 1
			return it
		end tell
		set each_hand to it
		set hand_variance to ""
		it * p
	end tell
end count_cards

to count_hands for p
	tell {}
		repeat p times
			set end to {}
		end repeat
		it
	end tell
end count_hands

to deal_cards from d to p thru c
	copy empty_hands to h
	tell h
		repeat with i from 0 to c - 1
			set item (i mod p + 1)'s end to d's item (i + 1)
		end repeat
		it
	end tell
end deal_cards

to sort_cards from d for p
	copy empty_hands to h
	script o
		property l : d
		property r : h
	end script
	considering case
		repeat with n from 1 to 52
			tell my full_deck's item n to repeat with i from 1 to p
				if it is in o's l's item i then
					set o's r's item i's end to it
					exit repeat
				end if
			end repeat
		end repeat
	end considering
	h
end sort_cards

to show_cards of l for p given d:d, g:g, c:c, h:h
	set tid to text item delimiters
	set text item delimiters to ", "
	repeat with i from 1 to p
		set l's item i to "Player " & i & return & l's item i
	end repeat
	set text item delimiters to return & return
	tell l to set l to beginning & ({""} & rest)
	set text item delimiters to tid
	tell button returned of (display dialog "Game " & g & " ¢ Deal " & d & h & return & ¬
		return & l buttons {"End Play", "New Game.", "New Deal."} default button 3)
		if it is "End Play" then error number -128
		if it is "New Game." then return my (new_game given game_number:g + 1)
	end tell
end show_cards

on new_game given game_number:g
	set every_player to count_(every_player)
	set total_cards to count_cards of each_hand for every_player
	set empty_hands to count_hands for every_player
	tell every_player to set hand_info to " ¢ Cards: " & total_cards & " (" & it & ¬
		{" hands of " & each_hand & hand_variance, " hand"}'s item (1 div it + 1) & ")"
	set current_deck to my full_deck's items
	set deal_number to 1
	repeat
		shuffle_(current_deck)
		set every_hand to deal_cards to every_player from current_deck thru total_cards
		set every_hand to sort_cards from every_hand for every_player
		show_cards of every_hand for every_player given g:g, d:deal_number, c:total_cards, h:hand_info
		set deal_number to deal_number + 1
	end repeat
end new_game

new_game given game_number:1

Holly Mackerel Aquaman!

What I want to know is what books, links or other extraterrestrial, osmosis, implant/method do you guys have/use?

I mean, really

and

:o

Where do you guys get this stuff!? This is why, in 1980, I decided to get out of computer science.:o

Wow. Thanks. When my brain stopppssss hemoraging, I’ll try once again to see if I can follow this.:lol:

PreTech

:lol::lol::lol::lol::lol:

Sorry for that, PreTech - but your reaction made it all worthwhile. :wink:

I’ll come back to you on this - but it’s kinda late here and I have to get to planet Zark. Um… I mean - get to bed.
(Dang, nearly slipped up there - but I don’t think he noticed!)

If, after going through the stuff, you have any more questions, let me know. :slight_smile: