Ex45 "NameError: name 'myscene' is not defined

UPDATE: This is an error to do with importing files. It runs fine as a single file. I also have a two-file version that runs OK until it gets to the time.sleep() interval near the end.

I’m part way into Ex45. My code is fine syntax-wise, but for me the logic is the biggest problem. This is my first real programming language (I can do some stuff in R and MySQL).

I based my game very much on the Gothon one, in large part because I stuggle a bit with the logic. So, it’s not as original as some but I am learning something in the process.

Error is:
ex45_maps.py", line 4, in Map
‘intro’: Intro(),
NameError: name ‘Intro’ is not defined

‘intro’ is the equivalent of ‘central_corridor’. It is defined as a class like CentralCorridor. It is called by a_game.play

I have four files:
The base file with imports and code to run the game (ex45.py).
The engine
The map file
The scenes.

ex45.py:

from sys import exit
from random import randint
from textwrap import dedent 

from ex45_engine import *
from ex45_maps import *
from ex45_scenes import *

a_map = Map('intro') # a_map is a class Map called 'intro'
a_game = Engine(a_map) # a_game is a class Engine that calls a_map
a_game.play() # from a_game get the play function

ex45_engine.py

class Engine(object):

    def __init__(self, scene_map): 
        self.scene_map = scene_map  
    
    def play(self): 
        current_scene = self.scene_map.opening_scene() 
        last_scene = self.scene_map.next_scene('success')  
    
        while current_scene != last_scene:
            next_scene_name = current_scene.enter()
            current_scene = self.scene_map.next_scene(next_scene_name)
        
        # be sure to print out the last scene
        current_scene.enter()

ex45_maps.py

class Map(object):

    scenes = {
        'intro': Intro(),
        'village': Village(),
        'hunker_down': HunkerDown(),
        'artillery_observation': ArtilleryObservation(),
        'fire_control_one': FireControlOne(),
        'fire_control_two': FireControlTwo(),
        'observe_ffe': ObserveFFE(),
        'failure': Failure(),
        'success': Success(),
    }

    def __init__(self, start_scene):
        self.start_scene = start_scene

    def next_scene(self, scene_name): 
        val = Map.scenes.get(scene_name) 
        return val 
    
    def opening_scene(self):  
        return self.next_scene(self.start_scene) 

ex45_scenes.py

class Scene(object):

    def enter(self):
        print("This scene doesn't get called, at least not to print.") 
        print("Subclass it and implement enter().")
        exit(1) 
        
class Failure(Scene):

    def enter(self): 
        print("""
            Killed, wounded, taken captive or still out there? 
            We don't know, but Sergeant Lyev's mission was a brave one 
            and his name will be spoken of fondly by comrades as we march westwards.
            """)            
        exit()
        
class Success(Scene):

    def enter(self):
        print("""
            You and you men made a vital contribution to driving the fascists 
            from Smerovo! Onwards! For the motherland! To Berlin!
            """)
        exit()
        
class Intro(Scene):

    def enter(self):
        print("""
            On the Ukrainian front in April 1944, several companies of fascist forces (about 500 men) 
            occupy the village of Smerovo. Clear ground lies between the woods in which we have massed 
            a tank battalion (39 T-34 tanks) plus infantry and the edges of the village, 2km away 
            up a gentle slope. Captured fascist soldiers have told us they are well dug in with 
            anti-tank weapons and machine guns plus tanks and infantry in reserve in the village, 
            where the command post is set up. They expect an attack and are well-prepared, with 
            artillery reference points already set on the approaches to the village. The river Glavny 
            lies to the south (our left) of the village so approach from that side is not possible. 
            To all other sides of the village there are no natural obstacles for many kilometres. 
            Immediately in front of the village as we look at it are thickets that give good cover. 
            Behind them, rising higher, are walls and banks where the fascist defences are dug in.
            We have two companies of artillery at our disposal set up several km to our rear. 
            Our commanding officer has formulated a plan that requires a feint attack on the 
            village's defences in front of our positions while sending the bulk of our forces 
            around to attack their left flank. To do that depends on a reconnaissance group finding 
            the location of the command centre and calling in artillery to destroy communications 
            in the fascist rear. A reconnaissance squad of four men led by Sergeant Lyev will set off 
            after dark. Shall we brief Sgt Lyev? 
            .
            .
            .
            .
            .
            .
            Sgt Lyev, you must decide on the approach to take to the village. 
            There are thickets that give cover up to the walls of the village if you approach 
            it directly, but you will be in the open for some time and will have to find a gap 
            in the defences when you get there. You could also travel along the river bank. 
            It is muddy and may be visible to enemy forces on the other side of the river but 
            it is hidden by its steep banks from the village's defenders. Whatever you choose, 
            your squad will depart at 9pm under cover of darkness.
            """)
        
        action = input("direct or riverbank?> ")

        if action == "direct":
            print("""
                You walk, run and crawl across no-man's land for 1.5km before a German MG opens up 
                and pins you down, badly wounding one of your men. Luckily, the fire has been seen 
                and heard from our lines. A mortar barrage stops the machine gun, but the Germans 
                are twitchy. Shots ring out for some time. You stay still for a couple of hours before things 
                become quiet again. Finally, you make it through the undergrowth and the rest of the 
                way to the village walls and find a gap between dug-in fascist troops.
                """)
            return 'village'
        
        elif action == "riverbank":
            print("""
                Your team makes it 1km along the oozing, thick mud of the river bank 
                before being spotted by German soldiers on the other bank. They send up a flare 
                and open fire. You and one of your men are badly wounded and are dragged back to 
                your lines by the other men.
                """)
            return 'failure'
            
        else:
                print("Speak up, man!")
                return 'intro'
        
class Village(Scene):

    def enter(self):
        print("""
            You heard the voices of the Germans dug in to the right of you as you crept past 
            the outer defences. They were still tense and excited from the earlier firing. 
            Now all seems quiet in front of you as you peer into the gloom of the village. 
            But you must now choose which direction to go. From yard to yard among the houses 
            on the road towards the village centre, or left and through the back lanes?
            """)
        
        action = input("road or lanes?> ")
        
        if action == "road":
            print("""
                Going along the main road, all is quiet as you head towards the village centre. 
                You and your men move in and out between the wooden lap-built houses as you proceed. 
                Then, an eruption of squawking as Avimov stumbles against a chicken shed. You soon hear 
                German voices, agitated, pulling clothes on and readying weapons. You fire first, 
                but are soon spotted. A firefight ensues, with two of your men killed almost straight away. 
                You fight to the last bullet.
                """)
            return 'failure'     

        elif action == "lanes":
            print("""
                You move left, and lead your men towards the edges of the village, taking a round-about 
                route to cut back in further on. Taking each step carefully, each man tries to move as 
                silently as possible, to not breathe too loudly. Going behind the houses and nearer the 
                river you come to a wider road coming from the left. Peering right, about 75m away you 
                see officers' vehicles and some activity around a larger building in the village centre. 
                You note the coordinates of the command post and turn away towards the river, where you 
                will find a spot to dig in and radio them back to your regiment. You aim to be within 
                sight of the command post so you can guide artillery fire the next day. You are under 
                strict orders not to use the radio again until the morning.
                """)
            return 'hunker_down'
        
class HunkerDown(Scene):

    def enter(self):
        print("""            .
            Your squad succeeded in finding the fascist command post in Smerovo. You will surely 
            be recommended for an award. The first artillery barrage is scheduled for 3am. 
            This will target the village defences in front of the woods, lay down smoke to the 
            right, and attempt to knock out the command post in the village centre. At the same 
            time a third of our tanks plus infantry will head towards the village on a direct route. 
            Meanwhile, the main force will proceed under cover of smoke to the right of the village 
            where it will turn in to take the objective from the unprotected flank. The success of 
            that mission depends on you guiding the artillery to destroy the centre of the enemy's 
            command and communications in the village so that its reserve forces are not sent in 
            time to defend from the real attack.
            """)
        return 'artillery_observation'
        
class ArtilleryObservation(Scene):

    def enter(self):
        print("""
            Time to wake up!
            It is 0245. After dozing uncomfortably in a hedge by a wall in the cold dampness of 
            the April night you rouse your men into life. Soon the guns will start, but nothing stirs 
            for now. You stay hidden, waiting. At 0300 precisely all hell opens up to 
            the east. It's the first sheaf of shells aimed at the village defences. Then, a 
            louder whistling, seemingly heading straight for you, and a single shell slams into the 
            farmland somewhere west of the command post - to its left. It is the first range-finding 
            shell. You wind the radio into life and make contact with the artillery commander to give 
            him targeting corrections.
            """)
        return 'fire_control_one'
        
class FireControlOne(Scene):

    def enter(self):
        print("""
            You want the next shell to fall shorter. Do you tell him east 25m, 50m or 75m?
            """)

        action = input("25, 50 or 75?> ")
        
        if action == "25":
            print("""
            Within a minute you hear another shell whistling in. The ground shakes but 
            still it has over-shot the command post. 
            """)
            return 'fire_control_one'
            
        elif action == "50":
            print("""
            Within a minute you hear another shell whistling in. The ground shakes but 
            still it has over-shot the command post.
            """)
            return 'fire_control_one'
            
        elif action == "75":
            print("""
            Within a minute you hear another shell whistling in. The ground shakes and a 
            plume of dirt rises high in the air. But it looks quite far beyond the command 
            post building. You get the commander on the radio again to correct fire to 
            the south, towards the command post, but also towards your hiding place.
            """)
            return 'fire_control_two'

class FireControlTwo(Scene):

    def enter(self):
        print("""
            You want the next shell to fall shorter. Do you tell him north 25m, 50m or 75m?
            """)
            
        action = input("25, 50 or 75?> ")
        
        if action == "25":
            print("""
            Within 40 seconds another shell comes in. This time again it hits way off 
            to the other side of the command post. 
            """)
            return 'fire_control_two'
                
        elif action == "50":
            print("""
            Uurraaah! The single range-finding shell lands right next to the command post 
            sending a motorcycle and sidecar flying through the air. You get back on the 
            radio as quickly as possible to ask for a full sheaf of fire. Hunker down and 
            observe the fire.
            """)
            return 'observe_ffe'
                
        elif action == "75":
            print("""
            Seconds later. Your head feels like it is being hit with a hammer. You can hardly 
            see and you can't hear. You look around to see your men covered in dirt and all 
            of you scattered several metres from where you were hidden. You can see ripped 
            cloth and the red of exposed flesh on one man. The shell made its impact 30m 
            in front of you; too close. You feel a sense of panic as you look for the radio set.
            """)
            return 'failure' 

class ObserveFFE(Scene):

    def enter(self):
        print("""
        You watch as the shells come raining in. Soon the old building is destroyed, a 
        pile of burning wooden beams, the remains of vehicles blazing nearby. You feel pity 
        for any poor devils that got caught in that.  
        """)
            
        action = input("Press x to see damage report> " )
        
        damage = random.randint(0, 9)
        
        if damage <= 4:
            print("""
            A direct hit with the first sheaf! That artillery commander is good. You tell him 
            you'll drink vodka with him when you see him! 10 minutes later our tanks arrive 
            at the village centre and head off to the right. Success!
            """)
            return 'success'
            
        elif damage > 4:
            print("""
            A direct hit. You wait. And you wait some more. After a while you hear the sounds of 
            fighting to the north. Then you see aircraft diving in too – they look German. 
            Things don't look good. Something must have gone wrong and the attack has failed. 
            You'll need to get back to our lines, but probably best to wait until dark. 
            It's going to be a long time until you are safe again.
            """)
            return 'failure'

Hello @S2Stats.

I just guessing this is something that is not imported properly.
Try to add

from ex45_maps import x

x is the class or definition where “‘intro’: Intro()” is.
I had a similar problem where I think this extra import did the trick.

I am not sure about this. So if I am wrong please tell me.

That would work in other circumstances but I need all the code in the file to be imported.

Perhaps

from ex45_maps import *

“*” = everything in that file.

Of course! Thanks for the reminder.

Please tell me if this works.
I am far from an expert on this.
Just starting to see a fraction of light in the tunnel of knowledge.

It worked. Thanks for your help!

The programme has stopped at another error now, with time.sleep() not working, for no apparent reason. I’ve tried tweaking the way that imports too, but no luck.

I was looking at your code, and I’m curious, when you run this, Does the Engine. map, and scene from your imports run, or the ones you defined in the file?

Basically you have an imported class, “Engine” , imported “Map”, and “Scene” and defined new class’s by the same names. Which ones does python run? The ones from the imports, or the ones you defined in this file?

Also: Python reads from bottom up. So if your initiating the imports at the top, and intro isn’t defined yet in one of those imports, It will say: Hey this “Intro” isn’t defined yet. Try moving the top three class calls to the bottom and see what happens.
Could you Delete those imports and just run the ones you have defined here?

Apologies. I had the code in four separate files, hence the import lines. Does your question still apply when the imports are called from the game runner file (ex45.py)?

1 Like

So, I didn’t know they were all separate files. That parts ok then. What I did is copy and pasted your Map() class file, your Scenes file, and tried to get it to just initiate from the imported scenes.
It did fine on intro, but now it’s saying Briefing() is undefined. Did you change the name, or maybe I didn’t get it all copy and pasted correctly.
The way I had to get it to work is to import the Scenes into the Map file. All those scenes are defined in the Scene file, so the Map on stand alone has a bunch of undefined class’s in it. I tried commenting out the import of scenes in Map, and it failed. But if the Scenes are imported into Map, so that all those class’s are reachable, it works (other than the Briefing() I may be missing with the bad copy paste.)
So I think your Scenes has to be either in the same file, or imported in Map.

#from ex45_scenes import *   #<---  uncomment to see it work or fail with comment.
class Map(object):

    scenes = {
        'intro': Intro(),
        'briefing': Briefing(),
        'village': Village(),
        'hunker_down': HunkerDown(),
        'artillery_observation': ArtilleryObservation(),
        'fire_control_one': FireControlOne(),
        'fire_control_two': FireControlTwo(),
        'observe_ffe': ObserveFFE(),
        'failure': Failure(),
        'success': Success(),
    }

    def __init__(self, start_scene):
        self.start_scene = start_scene

    def next_scene(self, scene_name):
        val = Map.scenes.get(scene_name)
        return val

    def opening_scene(self):
        return self.next_scene(self.start_scene)

test = Map('intro') #<-- comment and uncomment to see the effect of import

and to test that the imports worked: This:

from ex45_maps import *
from ex45_scenes import *

a_map = Map('intro')

It looks like you got it working with the import * (import all). But this could be a useful test to make sure files and imports all cooperate.

Thanks for your help on this! Much appreciated. I think the lesson to learn is that I was maybe over-ambitious trying to split all the classes up like that, without thinking about the order and to where things were imported.
Now that (ex45.py) runs but falls over when it comes to the time.sleep() function I put in it.
Which is funny, because it works in my all-in-one-file version.
But I’ll leave solving that for another day.

1 Like