Re: [Tutor] which of these is more efficient?

2019-08-19 Thread nathan tech
Hello everyone,

thank you so much for your responses.

When I said efficient, I meant in terms of storage, both in a file for 
saving maps if the user chooses to save, and in the computers memory. 
Low memory programs=happy users. sometimes.

These responses have been really helpful, and I did not realise I could 
do something like:

key=(x, y)

game_map[key]=bla.


LEarned a lot, so thanks!

Nathan

On 19/08/2019 15:02, Mats Wichmann wrote:
> On 8/18/19 5:55 PM, nathan tech wrote:
>> Hi there,
>>
>> So I am running over some coding ideas in my head for creating a map for
>> a game.
>>
>> This map would expand based on how far the user explores.
>>
>> I figure there are two ways to do this:
>>
>> 1: the list method:
>>
>> map=[]
>> for x in range(3):
>>    temp=[]
>>    for y in range(3):
>>     temp.append(default_grid_format)
>>    map.append(temp)
>>
>> then when ever the user explores a square not on the current map, it
>> would do this:
>>
>> for x in range(len(map)):
>>    map[x].append(default_grid_format)
>> temp=[]
>> for x in range(len(map[0])):
>>    temp.append(default_grid_format)
>> map.append(temp)
>>
>> Obviously, though, this creates a lot of data for squares that are still
>> ultimately unexplored.
>>
>> So here was my other idea:
>>
>> 2. the dictionary method:
>>
>> map={}
>> for x in range(3):
>>    for y in range(3):
>>     key=str(x)+":"+str(y)
>>     map[key]=default_grid_format
>>
>> Then when user explores new square do:
>>
>> key=str(player_x)+":"+str(player_y)
>> map[key]=default_grid_format
>>
>>
>> Is this an efficient method compared to 1?
>>
>> Is it, code wise, sound logic?
>>
>>
>>
>> I guess I'm just looking for a second opinion from experienced peoples.
> A few notes...
>
> There's a nice little tension in design and programming of a project:
> don't optimize too early, you may not need to, and/or your early guesses
> may be completely wrong VS. if you pick a bad design up front, it may be
> "too late" to optimize away the problems that brings later.  Then again
> they _also_ say "build one to throw away"  :)
>
> You haven't defined what needs to happen for a new square.  If the "map"
> (please don't use that as a variable name, by the way, as it's already a
> Python builtin function) is a rectangular grid, each square has eight
> neighbors.  If you've ever played with minesweeper, you see this in
> action... "exploring" from one point means figuring out which of the
> neighbors has a mine.  So what should happen for a "new" square? Do you
> want to generate the information for all of those neighbors? Your
> conceptual code seems to just add a row-and-column, which seems a little
> odd... what if the exploration went in the other direction, i.e. "left"
> or "up"?
>
> Python has some support for "don't calculate it until it's needed",
> called generators. Depending on your design you may or may not be able
> to make use of such.  Here's a silly little snippet to illustrate a
> generator which produces the neighbors of a square (in this case, just
> the coordinates):
>
> def neighbors(point):
>  x, y = point
>  yield x + 1, y
>  yield x - 1, y
>  yield x, y + 1
>  yield x, y - 1
>  yield x + 1, y + 1
>  yield x + 1, y - 1
>  yield x - 1, y + 1
>  yield x - 1, y - 1
>
> nlist = neighbors(point)
> print(f"neighbors returns {nlist}")
> print(f"neighbors of {point}: {list(nlist)}")
>
> When run:
>
> neighbors returns 
> neighbors of (7, 6): [(8, 6), (6, 6), (7, 7), (7, 5), (8, 7), (8, 5),
> (6, 7), (6, 5)]
>
> ... the data (just the coordinates of a neighbor square) is not
> generated until you consume it, which the string in the print call
> forces by calling list() on it. The more normal case is you'd loop over
> it...
>
> It this was too complicated for where you are in your Python studies,
> don't worry about it, but you'll get there eventually :)
>
>
> Micro-comment: a piece of code I'd hope to never see again:
>
> for x in range(len(map)):
> map[x].append(default_grid_format)
>
> this lazily produces a counter from the size of an iterable object
> (similar in concept to a generator), and then uses the counter to index
> into said object; that's not needed since you can just iterate the
> object directly.  Instead write it like this:
>
> for m in map:
>m.append(default_grid_format)
>
>
> Micro-comment: you can use anything (well, anything "hashable") as the
> key for a dict.  So for:
>
> key=str(x)+":"+str(y)
>
> you can just do:
>
> key = (x, y)
>
>
> ___
> Tutor maillist  -  Tutor@python.org
> To unsubscribe or change subscription options:
> https://mail.python.org/mailman/listinfo/tutor
___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] which of these is more efficient?

2019-08-19 Thread Mats Wichmann
On 8/18/19 5:55 PM, nathan tech wrote:
> Hi there,
> 
> So I am running over some coding ideas in my head for creating a map for 
> a game.
> 
> This map would expand based on how far the user explores.
> 
> I figure there are two ways to do this:
> 
> 1: the list method:
> 
> map=[]
> for x in range(3):
>   temp=[]
>   for y in range(3):
>    temp.append(default_grid_format)
>   map.append(temp)
> 
> then when ever the user explores a square not on the current map, it 
> would do this:
> 
> for x in range(len(map)):
>   map[x].append(default_grid_format)
> temp=[]
> for x in range(len(map[0])):
>   temp.append(default_grid_format)
> map.append(temp)
> 
> Obviously, though, this creates a lot of data for squares that are still 
> ultimately unexplored.
> 
> So here was my other idea:
> 
> 2. the dictionary method:
> 
> map={}
> for x in range(3):
>   for y in range(3):
>    key=str(x)+":"+str(y)
>    map[key]=default_grid_format
> 
> Then when user explores new square do:
> 
> key=str(player_x)+":"+str(player_y)
> map[key]=default_grid_format
> 
> 
> Is this an efficient method compared to 1?
> 
> Is it, code wise, sound logic?
> 
> 
> 
> I guess I'm just looking for a second opinion from experienced peoples.

A few notes...

There's a nice little tension in design and programming of a project:
don't optimize too early, you may not need to, and/or your early guesses
may be completely wrong VS. if you pick a bad design up front, it may be
"too late" to optimize away the problems that brings later.  Then again
they _also_ say "build one to throw away"  :)

You haven't defined what needs to happen for a new square.  If the "map"
(please don't use that as a variable name, by the way, as it's already a
Python builtin function) is a rectangular grid, each square has eight
neighbors.  If you've ever played with minesweeper, you see this in
action... "exploring" from one point means figuring out which of the
neighbors has a mine.  So what should happen for a "new" square? Do you
want to generate the information for all of those neighbors? Your
conceptual code seems to just add a row-and-column, which seems a little
odd... what if the exploration went in the other direction, i.e. "left"
or "up"?

Python has some support for "don't calculate it until it's needed",
called generators. Depending on your design you may or may not be able
to make use of such.  Here's a silly little snippet to illustrate a
generator which produces the neighbors of a square (in this case, just
the coordinates):

def neighbors(point):
x, y = point
yield x + 1, y
yield x - 1, y
yield x, y + 1
yield x, y - 1
yield x + 1, y + 1
yield x + 1, y - 1
yield x - 1, y + 1
yield x - 1, y - 1

nlist = neighbors(point)
print(f"neighbors returns {nlist}")
print(f"neighbors of {point}: {list(nlist)}")

When run:

neighbors returns 
neighbors of (7, 6): [(8, 6), (6, 6), (7, 7), (7, 5), (8, 7), (8, 5),
(6, 7), (6, 5)]

... the data (just the coordinates of a neighbor square) is not
generated until you consume it, which the string in the print call
forces by calling list() on it. The more normal case is you'd loop over
it...

It this was too complicated for where you are in your Python studies,
don't worry about it, but you'll get there eventually :)


Micro-comment: a piece of code I'd hope to never see again:

for x in range(len(map)):
   map[x].append(default_grid_format)

this lazily produces a counter from the size of an iterable object
(similar in concept to a generator), and then uses the counter to index
into said object; that's not needed since you can just iterate the
object directly.  Instead write it like this:

for m in map:
  m.append(default_grid_format)


Micro-comment: you can use anything (well, anything "hashable") as the
key for a dict.  So for:

   key=str(x)+":"+str(y)

you can just do:

   key = (x, y)


___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] which of these is more efficient?

2019-08-19 Thread Alan Gauld via Tutor
On 19/08/2019 00:55, nathan tech wrote:

> Is this an efficient method compared to 1?

Efficient in what sense?

The amount of code to write?
The amount of computing resources used?
The speed - and does that matter in an interactive game like this?


-- 
Alan G
Author of the Learn to Program web site
http://www.alan-g.me.uk/
http://www.amazon.com/author/alan_gauld
Follow my photo-blog on Flickr at:
http://www.flickr.com/photos/alangauldphotos


___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] which of these is more efficient?

2019-08-19 Thread Mark Lawrence

On 19/08/2019 00:55, nathan tech wrote:

Hi there,

So I am running over some coding ideas in my head for creating a map for
a game.

This map would expand based on how far the user explores.

I figure there are two ways to do this:

1: the list method:

map=[]

for x in range(3):

   temp=[]

   for y in range(3):

    temp.append(default_grid_format)

   map.append(temp)


then when ever the user explores a square not on the current map, it
would do this:

for x in range(len(map)):

   map[x].append(default_grid_format)

temp=[]

for x in range(len(map[0])):

   temp.append(default_grid_format)

map.append(temp)

Obviously, though, this creates a lot of data for squares that are still
ultimately unexplored.

So here was my other idea:


2. the dictionary method:

map={}

for x in range(3):

   for y in range(3):

    key=str(x)+":"+str(y)

    map[key]=default_grid_format


Then when user explores new square do:

key=str(player_x)+":"+str(player_y)

map[key]=default_grid_format


Is this an efficient method compared to 1?

Is it, code wise, sound logic?



I guess I'm just looking for a second opinion from experienced peoples.

thanks everyone.

Nathan



Quite frankly I've no idea as in 19 years of using Python my gut has 
never once been correct about code efficiency, so I suggest that you try 
it and see.  Start with https://docs.python.org/3/library/timeit.html. 
Of course this assumes that you need to do this in the first place :)


--
My fellow Pythonistas, ask not what our language can do for you, ask
what you can do for our language.

Mark Lawrence

___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] which of these is more efficient?

2019-08-19 Thread Peter Otten
nathan tech wrote:

> Hi there,
> 
> So I am running over some coding ideas in my head for creating a map for
> a game.
> 
> This map would expand based on how far the user explores.
> 
> I figure there are two ways to do this:
> 
> 1: the list method:
> 
> map=[]
> 
> for x in range(3):
> 
>  temp=[]
> 
>  for y in range(3):
> 
>  temp.append(default_grid_format)
> 
>  map.append(temp)
> 
> 
> then when ever the user explores a square not on the current map, it
> would do this:
> 
> for x in range(len(map)):
> 
>  map[x].append(default_grid_format)
> 
> temp=[]
> 
> for x in range(len(map[0])):
> 
>  temp.append(default_grid_format)
> 
> map.append(temp)
> 
> Obviously, though, this creates a lot of data for squares that are still
> ultimately unexplored.
> 
> So here was my other idea:
> 
> 
> 2. the dictionary method:
> 
> map={}
> 
> for x in range(3):
> 
>  for y in range(3):
> 
>  key=str(x)+":"+str(y)
> 
>  map[key]=default_grid_format
> 
> 
> Then when user explores new square do:
> 
> key=str(player_x)+":"+str(player_y)
> 
> map[key]=default_grid_format
> 
> 
> Is this an efficient method compared to 1?
> 
> Is it, code wise, sound logic?
> 
> 
> 
> I guess I'm just looking for a second opinion from experienced peoples.
> 
> thanks everyone.

Forget about "efficiency", try to write clean code.
For the above samples this means

- no unused data
- no unnecessary stringification

If you base your code on a defaultdict cells spring into existence as 
needed:

$ cat tmp.py
from collections import defaultdict

def get_default_format():
   return "whatever"

def show_cell(x, y):
print(f"x = {x}, y = {y}, format = {grid_formats[x,y]!r}")

grid_formats = defaultdict(get_default_format)
grid_formats[42, 42] = "this cell is special"

player_location = 3, 4
show_cell(*player_location)
show_cell(42, 42)

$ python3.6 tmp.py
x = 3, y = 4, format = 'whatever'
x = 42, y = 42, format = 'this cell is special'


___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor