PDA

View Full Version : Coding Amateur Coder Requests Assistance (Python3)



ThreadNecro5
2020-09-12, 10:42 PM
So first I apologise if this is the wrong place to put this but as far as I can tell my question fits this section's purpose.

So, context, I am new at coding and am using Python 3 (or whatever the latest one is) and PyCharm. At present I am producing a dice roller for practice and am experiencing several issues. If there is a better way to display my code Please inform me and I can edit it in here.

So I have things mostly working using the following preliminary code (do note I cannot indent within the text box so '-' represents a space):

#import random
d4 = i[1:4]
print(d4)
dice = input("choose dice:")
if dice == "d4":
----dice = range[1:4]
if dice == "d6":
----dice = (1, 2, 3, 4, 5, 6)
if dice == "d8":
----dice = (1, 2, 3, 4, 5, 6, 7, 8)
if dice == "d10":
----dice = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
if dice == "d12":
----dice = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12)
if dice == "d20":
----dice = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20)
dice = ()print ("Original List is:" +str(dice))
rand_idx = random.randrange(len(dice))
random_num = dice[rand_idx]
print("Random Selected Number is :" +str(random_num))



As you likely notice the d4 is presently nonfunctional as I am using it as the one I test new code on first.

So my main issue is that I am trying to replace the number lists for each die type with a slice of a 1-100 count to save space and make the addition of a d100 feasible (I am correct in assuming this idea would result in more efficient code?). So Ultimately every variation of getting this idea to work results in the error message that the code is unsubscriptable. To my knowledge I am working with lists which are supposed to accept subscript and every other solution I have tried has resulted in the same error. I have tried converting the numbers into a float to make sure I am not dealing with an integer, for example. To me this suggests the issue is sourced from a direction I am not looking.

My other main issue is in this:
for i in range (1, 101):

So while trying to get the above to behave as intended I keep getting the message in PyCharm that i is not defined enough or something similar (of course when I try and recreate one of the things I tried before to get the exact wording I cannot) and it tends to happen when I try to name i (There is a term for this but I forget. I'm new at this).

Thank you in advance for any assistance offered.

Ibrinar
2020-09-13, 06:53 AM
Use code tags if you want indentation, the one with the #.


dice = input("choose dice:")
if dice == "d4":
dice = list(range(1,5))
if dice == "d6":
dice = (1, 2, 3, 4, 5, 6)
if dice == "d8":


First use elif. It doesn't matter too much here but if you test the value and then change it you probably don't want to apply the rest of the tests to it (both because it is a waste of time and because in other scenarios the change might mean one of the other conditions applies. Also that allows adding an else for a default reaction.)

If you want an list in a range you can do dice = list(range(1,5)) (5 because the upper end is not included). Though you don't need any lists if you use randrange, you can just do random.randrange(1,4) and replace the 4 with a variable if you want.

It being unsubscriptable in this iteration comes because you use it on range use range with () not [].

For your second problem, please post a code sample that give the error.

Florian
2020-09-13, 07:08 AM
Ok, work with ranges.

For 1d4

Import Random
Min = 1
Max = 4
Print Random Randint (Min, Max)

Setting the random range to integers solves your problem.

Manga Shoggoth
2020-09-13, 08:04 AM
So my main issue is that I am trying to replace the number lists for each die type with a slice of a 1-100 count to save space and make the addition of a d100 feasible (I am correct in assuming this idea would result in more efficient code?). So Ultimately every variation of getting this idea to work results in the error message that the code is unsubscriptable. To my knowledge I am working with lists which are supposed to accept subscript and every other solution I have tried has resulted in the same error. I have tried converting the numbers into a float to make sure I am not dealing with an integer, for example. To me this suggests the issue is sourced from a direction I am not looking.

Generally speaking, each type of die (dN) gives you a random number between 1 and N.

What you are doing is:

List all face values for each kind of dice up to your maximum range (100)
Generate a random number X between 1 and N
Get the value from the Xth position of the list


... When you don't need a list of faces at all - all you really need to do is:

Generate a random number between 1 and N


That will give you any standard die range, and you don't have to generate a list with 100 entries for a D100. You can also generate any non-standard range you like (d45, d1000) without having to worry about the size of your list.

After that you need to start handling exceptions and variations. For example:


A typical d10 actually gives 0 - 9, so depending on how you are using the d10 you may want to subtract 1 from the above result.
Some dice have non-linear numbering or repeating numbers (for example, an averaging die uses 2, 3, 3, 4, 4, 5, and a doubling cube - admittedly not thrown as a dice - uses 2, 4, 8, 16, 32, 64) - in that event you skip the straight random number generation and use the method you have already coded with the actual face values.
How you are going to handle combinations of dice (percentiles and so on) - which you might need to code at a higher level.

Jasdoif
2020-09-14, 11:52 AM
rand_idx = random.randrange(len(dice))
random_num = dice[rand_idx]I'd recommend using random.choice (https://docs.python.org/3/library/random.html#random.choice) , here; saves you the trouble of generating a random index yourself.


So Ultimately every variation of getting this idea to work results in the error message that the code is unsubscriptable. To my knowledge I am working with lists which are supposed to accept subscript and every other solution I have tried has resulted in the same error.Your code is using tuples, not lists, actually; a list is surrounded by square brackets. But tuples accept subscripts as well so that wouldn't be causing your problem....My largely baseless guess is that your attempts have been missing the parentheses after a function name, so the code is trying to subscript the function object instead of the return value.

For example....


def testFunction():
return [1,2]

testFunction[0] gives the "object is not subscriptable" error because it's trying to select index 0 of the function object.
testFunction()[0] works correctly because it's trying to select index 0 of the list the function returns.

ThreadNecro5
2020-09-14, 06:25 PM
Use code tags if you want indentation, the one with the #.

Thank you, did not know that one existed. I know there was a page listing call code the sight uses but I lost track of the page around a site update one time.


So Thank you everyone who has offered assistance. After tinkering with things the following seems the easiest way to get it to do as I want, although I was unable to get Florian's suggestion working.



import random
dice = input("choose dice:")
if dice == "d4":
dice = random.randrange(1, 5)
print(dice)
elif dice == "d6":
dice = random.randrange(1, 7)
print(dice)

Ibrinar
2020-09-15, 04:37 AM
Btw there isn't much reason to deal which each dice separately just extract the number. If you want to restrict the dice types make an allowed list and check. You could do it like this.



import random
allowedDice=[4,6,8,10,12,20,100]
dice = input("choose dice:")
if dice[0] == "d":
size=int(dice[1:])
if(size in allowedDice):
result = random.randrange(1, size+1)
print("rolled ",dice," result=",result)
else:
print("Invalid size")
else:
print("Unrecognized command")


I didn't check whether what comes after d actually is a number though so int might give an exception if the input in wrong.

Instead of giving the allowed dice as numbers you could give it as "d4","d6".... and check the input directly, that would also prevent feeding anything into int that isn't an int. But numbers were less to type. (And unless you want to accept more commands than just dices I would only use the number as input.)

ThreadNecro5
2020-09-15, 06:52 PM
Btw there isn't much reason to deal which each dice separately just extract the number. If you want to restrict the dice types make an allowed list and check. You could do it like this.



import random
allowedDice=[4,6,8,10,12,20,100]
dice = input("choose dice:")
if dice[0] == "d":
size=int(dice[1:])
if(size in allowedDice):
result = random.randrange(1, size+1)
print("rolled ",dice," result=",result)
else:
print("Invalid size")
else:
print("Unrecognized command")


I didn't check whether what comes after d actually is a number though so int might give an exception if the input in wrong.

Instead of giving the allowed dice as numbers you could give it as "d4","d6".... and check the input directly, that would also prevent feeding anything into int that isn't an int. But numbers were less to type. (And unless you want to accept more commands than just dices I would only use the number as input.)

I had considered something like this but decided to wait until the rest of the code is done first. For the sake of learning I plan to turn my dice roller into a desktop app and do not know which method will work better for that end.

As to the code I have the dice roll and selection half done but have one last issue. I am adding a feature to ask the user if they wish to apply a modifier, and if no stop, and if yes create a pair of inputs for bonuses and penalties to apply to the previously generated number.


apply_mod = input("Apply Modifier? yes/no")
yes, y = True
no, n = False
if apply_mod is False:
print("No Modifier Selected")
else:
mod_bonus = int(input("Input Bonus"))
mod_penalty = int(input("Input Penalty"))
final_modifier = int(mod_bonus - mod_penalty)
print("Modified roll is: " + str(final_modifier))

which experiences the following issue 'line 22, in <module> yes, y = True
TypeError: cannot unpack non-iterable bool object'

I have tried using int but that did not help. A lot of my issues are probably basic things but I am having trouble actually finding the solutions to learn from my mistakes.

While I'm here if anyone can offer advice on things easily missed by a complete newbie that would be apricated.

DeTess
2020-09-16, 05:49 AM
apply_mod = input("Apply Modifier? yes/no")
yes, y = True
no, n = False
if apply_mod is False:
print("No Modifier Selected")
else:
mod_bonus = int(input("Input Bonus"))
mod_penalty = int(input("Input Penalty"))
final_modifier = int(mod_bonus - mod_penalty)
print("Modified roll is: " + str(final_modifier))

which experiences the following issue 'line 22, in <module> yes, y = True
TypeError: cannot unpack non-iterable bool object'


I don't think the second and third line of that snippet do what you think it does. It seems you're trying to check the value of apply_mod and then set something else to that value, right?

But unless I'm mistaken (I've mostly work with python 2) you're actually telling the code 'treat True as an iterable object and assign the first and second values of this object to the variables yes and y'.

jayem
2020-09-20, 05:32 AM
I don't think the second and third line of that snippet do what you think it does. It seems you're trying to check the value of apply_mod and then set something else to that value, right?

But unless I'm mistaken (I've mostly work with python 2) you're actually telling the code 'treat True as an iterable object and assign the first and second values of this object to the variables yes and y'.

That sounds right, and would explains the message. There isn't a first and second value of true.

Lessons:
If you don't have quotation marks around something, it almost definitely won't be a created string (you could go to extreme lengths to create a counter example)
If you want to compare (test equality), you need == in python
If you want to assign (declare equality), you want the assigned value on the left

Think about the natural order in if statement, and whether a double negative is (is False, interesting case in the Else) prettier than being direct. It often will be, it often won't. Also take a bit of care if you do reverse the conditions the opposite of > is <= not <.



...
if apply_mod=="yes":
apply_mod=True
elif apply_mod=="no"
apply_mod=False
else:
print("I need to think/learn how to handle odd cases")

if apply_mod==False:
...

should work (most of the time, as an exercise, what do you think happens for an input of "fish"), and not introduce anything new.

On this project, I'd be inclined to as you pick up more and more to go back to the start, perhaps once or twice.
You need to get some kind of balance, and just because you've learned how to use a chainsaw it doesn't mean every problem is a tree. I've definitely been stuck in the position where I spend too long trying to get the perfect general/specific foundation and then not enough time using it.