<commit_before>"""The dcm2nii module provides basic functions for dicom conversion

   Change directory to provide relative paths for doctests
   >>> import os
   >>> filepath = os.path.dirname( os.path.realpath( __file__ ) )
   >>> datadir = os.path.realpath(os.path.join(filepath, '../testing/data'))
   >>> os.chdir(datadir)
"""

from nipype.interfaces.base import (CommandLine, CommandLineInputSpec,
                                    InputMultiPath, traits, TraitedSpec,
                                    OutputMultiPath, isdefined,
                                    File, Directory)
import os
from copy import deepcopy
from nipype.utils.filemanip import split_filename
import re

class Dcm2niiInputSpec(CommandLineInputSpec):
    source_names = InputMultiPath(File(exists=True), argstr="%s", position=10,
                                  copyfile=False, mandatory=True)
    gzip_output = traits.Bool(False, argstr='-g', position=0, usedefault=True)
    nii_output = traits.Bool(True, argstr='-n', position=1, usedefault=True)
    anonymize = traits.Bool(argstr='-a', position=2)
    id_in_filename = traits.Bool(False, argstr='-i', usedefault=True, position=3)
    reorient = traits.Bool(argstr='-r', position=4)
    reorient_and_crop = traits.Bool(argstr='-x', position=5)
    output_dir = Directory(exists=True, argstr='-o %s', genfile=True, position=6)
    config_file = File(exists=True, argstr="-b %s", genfile=True, position=7)
    convert_all_pars = traits.Bool(argstr='-v', position=8)
    args = traits.Str(argstr='%s', desc='Additional parameters to the command',
                      position=9)

class Dcm2niiOutputSpec(TraitedSpec):
    converted_files = OutputMultiPath(File(exists=True))
    reoriented_files = OutputMultiPath(File(exists=True))
    reoriented_and_cropped_files = OutputMultiPath(File(exists=True))
    bvecs = OutputMultiPath(File(exists=True))
    bvals = OutputMultiPath(File(exists=True))

class Dcm2nii(CommandLine):
    """Uses MRICRON's dcm2nii to convert dicom files

    Examples
    ========

    >>> from nipype.interfaces.dcm2nii import Dcm2nii
    >>> converter = Dcm2nii()
    >>> converter.inputs.source_names = ['functional_1.dcm', 'functional_2.dcm']
    >>> converter.inputs.gzip_output = True
    >>> converter.inputs.output_dir = '.'
    >>> converter.cmdline #doctest: +ELLIPSIS
    'dcm2nii -g y -n y -i n -o . -b config.ini functional_1.dcm functional_2.dcm'
    >>> converter.run() # doctest: +SKIP
    """

    input_spec=Dcm2niiInputSpec
    output_spec=Dcm2niiOutputSpec

    _cmd = 'dcm2nii'

    def _format_arg(self, opt, spec, val):
        if opt in ['gzip_output', 'nii_output', 'anonymize', 'id_in_filename', 'reorient', 'reorient_and_crop', 'convert_all_pars']:
            spec = deepcopy(spec)
            if val:
                spec.argstr += ' y'
            else:
                spec.argstr += ' n'
                val = True
        return super(Dcm2nii, self)._format_arg(opt, spec, val)

    def _run_interface(self, runtime):

        new_runtime = super(Dcm2nii, self)._run_interface(runtime)
        (self.output_files,
         self.reoriented_files,
         self.reoriented_and_cropped_files,
         self.bvecs, self.bvals) = self._parse_stdout(new_runtime.stdout)
        return new_runtime

    def _parse_stdout(self, stdout):
        files = []
        reoriented_files = []
        reoriented_and_cropped_files = []
        bvecs = []
        bvals = []
        skip = False
        last_added_file = None
        for line in stdout.split("\n"):
            if not skip:
                file = None
                if line.startswith("Saving "):
                    file = line[len("Saving "):]
                elif line.startswith("GZip..."):
                    #for gzipped outpus files are not absolute
                    if isdefined(self.inputs.output_dir):
                        output_dir = self.inputs.output_dir
                    else:
                        output_dir = self._gen_filename('output_dir')
                    file = os.path.abspath(os.path.join(output_dir,
                                                        line[len("GZip..."):]))
                elif line.startswith("Number of diffusion directions "):
                    if last_added_file:
                        base, filename, ext = split_filename(last_added_file)
                        bvecs.append(os.path.join(base,filename + ".bvec"))
                        bvals.append(os.path.join(base,filename + ".bval"))
                elif re.search('-->(.*)', line):
                    search = re.search('.*--> (.*)', line)
                    file = search.groups()[0]

                if file:
                    files.append(file)
                    last_added_file = file
                    continue

                if line.startswith("Reorienting as "):
                    reoriented_files.append(line[len("Reorienting as "):])
                    skip = True
                    continue
                elif line.startswith("Cropping NIfTI/Analyze image "):
                    base, filename = os.path.split(line[len("Cropping NIfTI/Analyze image "):])
                    filename = "c" + filename
                    reoriented_and_cropped_files.append(os.path.join(base, filename))
                    skip = True
                    continue

            skip = False
        return files, reoriented_files, reoriented_and_cropped_files, bvecs, bvals

    def _list_outputs(self):
        outputs = self.output_spec().get()
        outputs['converted_files'] = self.output_files
        outputs['reoriented_files'] = self.reoriented_files
        outputs['reoriented_and_cropped_files'] = self.reoriented_and_cropped_files
        outputs['bvecs'] = self.bvecs
        outputs['bvals'] = self.bvals
        return outputs

    def _gen_filename(self, name):
        if name == 'output_dir':
            return os.getcwd()
        elif name == 'config_file':
            config_file = "config.ini"
            f = open(config_file, "w")
            # disable interactive mode
            f.write("[BOOL]\nManualNIfTIConv=0\n")
            f.close()
            return config_file
        return None

<commit_msg>Address large file with dcm2nii _parse_stdout<commit_after>"""The dcm2nii module provides basic functions for dicom conversion

   Change directory to provide relative paths for doctests
   >>> import os
   >>> filepath = os.path.dirname( os.path.realpath( __file__ ) )
   >>> datadir = os.path.realpath(os.path.join(filepath, '../testing/data'))
   >>> os.chdir(datadir)
"""

from nipype.interfaces.base import (CommandLine, CommandLineInputSpec,
                                    InputMultiPath, traits, TraitedSpec,
                                    OutputMultiPath, isdefined,
                                    File, Directory)
import os
from copy import deepcopy
from nipype.utils.filemanip import split_filename
import re

class Dcm2niiInputSpec(CommandLineInputSpec):
    source_names = InputMultiPath(File(exists=True), argstr="%s", position=10,
                                  copyfile=False, mandatory=True)
    gzip_output = traits.Bool(False, argstr='-g', position=0, usedefault=True)
    nii_output = traits.Bool(True, argstr='-n', position=1, usedefault=True)
    anonymize = traits.Bool(argstr='-a', position=2)
    id_in_filename = traits.Bool(False, argstr='-i', usedefault=True, position=3)
    reorient = traits.Bool(argstr='-r', position=4)
    reorient_and_crop = traits.Bool(argstr='-x', position=5)
    output_dir = Directory(exists=True, argstr='-o %s', genfile=True, position=6)
    config_file = File(exists=True, argstr="-b %s", genfile=True, position=7)
    convert_all_pars = traits.Bool(argstr='-v', position=8)
    args = traits.Str(argstr='%s', desc='Additional parameters to the command',
                      position=9)

class Dcm2niiOutputSpec(TraitedSpec):
    converted_files = OutputMultiPath(File(exists=True))
    reoriented_files = OutputMultiPath(File(exists=True))
    reoriented_and_cropped_files = OutputMultiPath(File(exists=True))
    bvecs = OutputMultiPath(File(exists=True))
    bvals = OutputMultiPath(File(exists=True))

class Dcm2nii(CommandLine):
    """Uses MRICRON's dcm2nii to convert dicom files

    Examples
    ========

    >>> from nipype.interfaces.dcm2nii import Dcm2nii
    >>> converter = Dcm2nii()
    >>> converter.inputs.source_names = ['functional_1.dcm', 'functional_2.dcm']
    >>> converter.inputs.gzip_output = True
    >>> converter.inputs.output_dir = '.'
    >>> converter.cmdline #doctest: +ELLIPSIS
    'dcm2nii -g y -n y -i n -o . -b config.ini functional_1.dcm functional_2.dcm'
    >>> converter.run() # doctest: +SKIP
    """

    input_spec=Dcm2niiInputSpec
    output_spec=Dcm2niiOutputSpec

    _cmd = 'dcm2nii'

    def _format_arg(self, opt, spec, val):
        if opt in ['gzip_output', 'nii_output', 'anonymize', 'id_in_filename', 'reorient', 'reorient_and_crop', 'convert_all_pars']:
            spec = deepcopy(spec)
            if val:
                spec.argstr += ' y'
            else:
                spec.argstr += ' n'
                val = True
        return super(Dcm2nii, self)._format_arg(opt, spec, val)

    def _run_interface(self, runtime):

        new_runtime = super(Dcm2nii, self)._run_interface(runtime)
        (self.output_files,
         self.reoriented_files,
         self.reoriented_and_cropped_files,
         self.bvecs, self.bvals) = self._parse_stdout(new_runtime.stdout)
        return new_runtime

    def _parse_stdout(self, stdout):
        import re
        import os
        files = []
        reoriented_files = []
        reoriented_and_cropped_files = []
        bvecs = []
        bvals = []
        skip = False
        last_added_file = None
        for line in stdout.split("\n"):
            if not skip:
                file = None
                if line.startswith("Saving "):
                    file = line[len("Saving "):]
                elif line.startswith("GZip..."):
                    #for gzipped outpus files are not absolute
                    if isdefined(self.inputs.output_dir):
                        output_dir = self.inputs.output_dir
                    else:
                        output_dir = self._gen_filename('output_dir')
                    file = os.path.abspath(os.path.join(output_dir,
                                                        line[len("GZip..."):]))
                elif line.startswith("Number of diffusion directions "):
                    if last_added_file:
                        base, filename, ext = split_filename(last_added_file)
                        bvecs.append(os.path.join(base,filename + ".bvec"))
                        bvals.append(os.path.join(base,filename + ".bval"))
                elif re.search('.*->(.*)', line):
                    val = re.search('.*->(.*)', line)
                    val = val.groups()[0]
                    if isdefined(self.inputs.output_dir):
                        output_dir = self.inputs.output_dir
                    else:
                        output_dir = self._gen_filename('output_dir')
                    val = os.path.join(output_dir, val)
                    file = val

                if file:
                    if last_added_file and os.path.exists(file) and not last_added_file in file:
                        files.append(file)
                    last_added_file = file
                    continue

                if line.startswith("Reorienting as "):
                    reoriented_files.append(line[len("Reorienting as "):])
                    skip = True
                    continue
                elif line.startswith("Cropping NIfTI/Analyze image "):
                    base, filename = os.path.split(line[len("Cropping NIfTI/Analyze image "):])
                    filename = "c" + filename
                    reoriented_and_cropped_files.append(os.path.join(base, filename))
                    skip = True
                    continue
            skip = False
        return files, reoriented_files, reoriented_and_cropped_files, bvecs, bvals

    def _list_outputs(self):
        outputs = self.output_spec().get()
        outputs['converted_files'] = self.output_files
        outputs['reoriented_files'] = self.reoriented_files
        outputs['reoriented_and_cropped_files'] = self.reoriented_and_cropped_files
        outputs['bvecs'] = self.bvecs
        outputs['bvals'] = self.bvals
        return outputs

    def _gen_filename(self, name):
        if name == 'output_dir':
            return os.getcwd()
        elif name == 'config_file':
            config_file = "config.ini"
            f = open(config_file, "w")
            # disable interactive mode
            f.write("[BOOL]\nManualNIfTIConv=0\n")
            f.close()
            return config_file
        return None

<|endoftext|>


<commit_before>__version__ = '0.3.0'
<commit_msg>Micro version bump for #19<commit_after>__version__ = '0.3.1'
<|endoftext|>


<commit_before><commit_msg>[FIX]Model de rastreabilidade sem duplicata<commit_after><|endoftext|>


<commit_before># -*- coding: utf-8 -*-
# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-
# vi: set ft=python sts=4 ts=4 sw=4 et:
'''
Algorithms to compute statistics on :abbr:`fMRI (functional MRI)`

    Change directory to provide relative paths for doctests
    >>> import os
    >>> filepath = os.path.dirname(os.path.realpath(__file__))
    >>> datadir = os.path.realpath(os.path.join(filepath, '../testing/data'))
    >>> os.chdir(datadir)

'''
from __future__ import (print_function, division, unicode_literals,
                        absolute_import)

import numpy as np
import nibabel as nb

from .. import logging
from ..interfaces.base import (traits, TraitedSpec, BaseInterface,
                               BaseInterfaceInputSpec, File, InputMultiPath)
IFLOG = logging.getLogger('interface')

class SignalExtractionInputSpec(BaseInterfaceInputSpec):
    in_file = File(exists=True, mandatory=True, desc='4-D fMRI nii file')
    label_files = InputMultiPath(File(exists=True), mandatory=True,
                                desc='a 3-D label image, with 0 denoting '
                                'background, or a list of 3-D probability '
                                'maps (one per label) or the equivalent 4D '
                                'file.')
    class_labels = traits.List(mandatory=True,
                               desc='Human-readable labels for each segment '
                               'in the label file, in order. The length of '
                               'class_labels must be equal to the number of '
                               'segments (background excluded). This list '
                               'corresponds to the class labels in label_file '
                               'in ascending order')
    out_file = File('signals.tsv', usedefault=True, exists=False,
                    mandatory=False, desc='The name of the file to output to. '
                    'signals.tsv by default')
    include_global = traits.Bool(False, usedefault=True, mandatory=False,
                                 desc='If True, include an extra column '
                                 'labeled "global", with values calculated from the entire brain '
                                 '(instead of just regions)')
    detrend = traits.Bool(False, usedefault=True, mandatory=False,
                          desc='If True, perform detrending using nilearn.')

class SignalExtractionOutputSpec(TraitedSpec):
    out_file = File(exists=True, desc='tsv file containing the computed '
                    'signals, with as many columns as there are labels and as '
                    'many rows as there are timepoints in in_file, plus a '
                    'header row with values from class_labels')

class SignalExtraction(BaseInterface):
    '''
    Extracts signals over tissue classes or brain regions

    >>> seinterface = SignalExtraction()
    >>> seinterface.inputs.in_file = 'functional.nii'
    >>> seinterface.inputs.label_files = 'segmentation0.nii.gz'
    >>> seinterface.inputs.out_file = 'means.tsv'
    >>> segments = ['CSF', 'gray', 'white']
    >>> seinterface.inputs.class_labels = segments
    >>> seinterface.inputs.detrend = True
    >>> seinterface.inputs.include_global = True
    '''
    input_spec = SignalExtractionInputSpec
    output_spec = SignalExtractionOutputSpec

    def _run_interface(self, runtime):
        masker, global_masker = self._process_inputs()

        region_signals = masker.fit_transform(self.inputs.in_file)

        if global_masker != None:
            self.inputs.class_labels.insert(0, 'global')
            global_masker.fit()
            global_signal= global_masker.transform(self.inputs.in_file)
            region_signals = np.hstack((global_signal, region_signals))

        output = np.vstack((self.inputs.class_labels, region_signals.astype(str)))

        # save output
        np.savetxt(self.inputs.out_file, output, fmt=b'%s', delimiter='\t')
        return runtime

    def _process_inputs(self):
        ''' validate and  process inputs into useful form '''
        import nilearn.input_data as nl
        import nilearn.image as nli

        label_datas = [nb.load(nifti) for nifti in self.inputs.label_files]
        label_data = nli.concat_imgs(label_datas)

        # determine form of label files, choose appropriate nilearn masker
        if len(label_datas) == 1 and np.amax(label_data.get_data()) > 1: # 3d label file
            n_labels = np.amax(label_data.get_data())
            masker = nl.NiftiLabelsMasker(label_data)
        else: # one 4d file
            n_labels = label_data.get_data().shape[3]
            masker = nl.NiftiMapsMasker(label_data)
        masker.set_params(detrend=self.inputs.detrend)

        # check label list size
        if len(self.inputs.class_labels) != n_labels:
            raise ValueError('The length of class_labels {} does not '
                             'match the number of regions {} found in '
                             'label_files {}'.format(self.inputs.class_labels,
                                                    n_labels,
                                                    self.inputs.label_files))

        global_masker = None
        if self.inputs.include_global:
            global_label_data = label_data.get_data().clip(0, 1).sum(axis=3)
            global_label_data = global_label_data[:, :, :, np.newaxis] # add back 4th dimension
            global_label_data = nb.Nifti1Image(global_label_data, np.eye(4))
            global_masker = nl.NiftiMapsMasker(global_label_data, detrend=self.inputs.detrend)

        return masker, global_masker

    def _list_outputs(self):
        outputs = self._outputs().get()
        outputs['out_file'] = self.inputs.out_file
        return outputs
<commit_msg>add new param incl_shared_variance<commit_after># -*- coding: utf-8 -*-
# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-
# vi: set ft=python sts=4 ts=4 sw=4 et:
'''
Algorithms to compute statistics on :abbr:`fMRI (functional MRI)`

    Change directory to provide relative paths for doctests
    >>> import os
    >>> filepath = os.path.dirname(os.path.realpath(__file__))
    >>> datadir = os.path.realpath(os.path.join(filepath, '../testing/data'))
    >>> os.chdir(datadir)

'''
from __future__ import (print_function, division, unicode_literals,
                        absolute_import)

import numpy as np
import nibabel as nb

from .. import logging
from ..interfaces.base import (traits, TraitedSpec, BaseInterface,
                               BaseInterfaceInputSpec, File, InputMultiPath)
IFLOG = logging.getLogger('interface')

class SignalExtractionInputSpec(BaseInterfaceInputSpec):
    in_file = File(exists=True, mandatory=True, desc='4-D fMRI nii file')
    label_files = InputMultiPath(File(exists=True), mandatory=True,
                                desc='a 3-D label image, with 0 denoting '
                                'background, or a list of 3-D probability '
                                'maps (one per label) or the equivalent 4D '
                                'file.')
    class_labels = traits.List(mandatory=True,
                               desc='Human-readable labels for each segment '
                               'in the label file, in order. The length of '
                               'class_labels must be equal to the number of '
                               'segments (background excluded). This list '
                               'corresponds to the class labels in label_file '
                               'in ascending order')
    out_file = File('signals.tsv', usedefault=True, exists=False,
                    mandatory=False, desc='The name of the file to output to. '
                    'signals.tsv by default')
    incl_shared_variance = traits.Bool(True, usedefault=True, mandatory=False, desc='By default '
                                       '(True), returns simple time series calculated from each '
                                       'region independently (e.g., for noise regression). If '
                                       'False, returns unique signals for each region, discarding '
                                       'shared variance (e.g., for connectivity)')
    include_global = traits.Bool(False, usedefault=True, mandatory=False,
                                 desc='If True, include an extra column '
                                 'labeled "global", with values calculated from the entire brain '
                                 '(instead of just regions)')
    detrend = traits.Bool(False, usedefault=True, mandatory=False,
                          desc='If True, perform detrending using nilearn.')

class SignalExtractionOutputSpec(TraitedSpec):
    out_file = File(exists=True, desc='tsv file containing the computed '
                    'signals, with as many columns as there are labels and as '
                    'many rows as there are timepoints in in_file, plus a '
                    'header row with values from class_labels')

class SignalExtraction(BaseInterface):
    '''
    Extracts signals over tissue classes or brain regions

    >>> seinterface = SignalExtraction()
    >>> seinterface.inputs.in_file = 'functional.nii'
    >>> seinterface.inputs.label_files = 'segmentation0.nii.gz'
    >>> seinterface.inputs.out_file = 'means.tsv'
    >>> segments = ['CSF', 'gray', 'white']
    >>> seinterface.inputs.class_labels = segments
    >>> seinterface.inputs.detrend = True
    >>> seinterface.inputs.include_global = True
    '''
    input_spec = SignalExtractionInputSpec
    output_spec = SignalExtractionOutputSpec

    def _run_interface(self, runtime):
        masker, global_masker = self._process_inputs()

        region_signals = masker.fit_transform(self.inputs.in_file)

        if global_masker != None:
            self.inputs.class_labels.insert(0, 'global')
            global_masker.fit()
            global_signal= global_masker.transform(self.inputs.in_file)
            region_signals = np.hstack((global_signal, region_signals))

        output = np.vstack((self.inputs.class_labels, region_signals.astype(str)))

        # save output
        np.savetxt(self.inputs.out_file, output, fmt=b'%s', delimiter='\t')
        return runtime

    def _process_inputs(self):
        ''' validate and  process inputs into useful form '''
        import nilearn.input_data as nl
        import nilearn.image as nli

        label_datas = [nb.load(nifti) for nifti in self.inputs.label_files]
        label_data = nli.concat_imgs(label_datas)

        # determine form of label files, choose appropriate nilearn masker
        if len(label_datas) == 1 and np.amax(label_data.get_data()) > 1: # 3d label file
            n_labels = np.amax(label_data.get_data())
            masker = nl.NiftiLabelsMasker(label_data)
        else: # one 4d file
            n_labels = label_data.get_data().shape[3]
            masker = nl.NiftiMapsMasker(label_data)
        masker.set_params(detrend=self.inputs.detrend)

        # check label list size
        if len(self.inputs.class_labels) != n_labels:
            raise ValueError('The length of class_labels {} does not '
                             'match the number of regions {} found in '
                             'label_files {}'.format(self.inputs.class_labels,
                                                    n_labels,
                                                    self.inputs.label_files))

        global_masker = None
        if self.inputs.include_global:
            global_label_data = label_data.get_data().clip(0, 1).sum(axis=3)
            global_label_data = global_label_data[:, :, :, np.newaxis] # add back 4th dimension
            global_label_data = nb.Nifti1Image(global_label_data, np.eye(4))
            global_masker = nl.NiftiMapsMasker(global_label_data, detrend=self.inputs.detrend)

        return masker, global_masker

    def _list_outputs(self):
        outputs = self._outputs().get()
        outputs['out_file'] = self.inputs.out_file
        return outputs
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
'''
Primary interfaces for the salt-cloud system
'''
# Need to get data from 4 sources!
# CLI options
# salt cloud config - /etc/salt/cloud
# salt master config (for master integration)
# salt VM config, where VMs are defined - /etc/salt/cloud.profiles
#
# The cli, master and cloud configs will merge for opts
# the VM data will be in opts['profiles']

# Import python libs
from __future__ import print_function
import os
import sys
import getpass
import logging

# Import salt libs
import salt.config
import salt.output
import salt.utils
from salt import syspaths
from salt.utils import parsers
from salt.utils.validate.path import is_writeable
from salt.utils.verify import check_user, verify_env, verify_files

# Import salt.cloud libs
import salt.cloud
from salt.cloud.exceptions import SaltCloudException, SaltCloudSystemExit

log = logging.getLogger(__name__)

class SaltCloud(parsers.SaltCloudParser):

    def run(self):
        '''
        Execute the salt-cloud command line
        '''
        # Parse shell arguments
        self.parse_args()

        salt_master_user = self.config.get('user', getpass.getuser())
        if salt_master_user is not None and not check_user(salt_master_user):
            self.error(
                'salt-cloud needs to run as the same user as salt-master, '
                '{0!r}, but was unable to switch credentials. Please run '
                'salt-cloud as root or as {0!r}'.format(salt_master_user)
            )

        try:
            if self.config['verify_env']:
                verify_env(
                    [os.path.dirname(self.config['conf_file'])],
                    salt_master_user
                )
                logfile = self.config['log_file']
                if logfile is not None and not logfile.startswith('tcp://') \
                        and not logfile.startswith('udp://') \
                        and not logfile.startswith('file://'):
                    # Logfile is not using Syslog, verify
                    verify_files([logfile], salt_master_user)
        except (IOError, OSError) as err:
            log.error('Error while verifying the environment: {0}'.format(err))
            sys.exit(err.errno)

        # Setup log file logging
        self.setup_logfile_logger()

        if self.options.update_bootstrap:
            log.debug('Updating the bootstrap-salt.sh script to latest stable')
            import urllib2
            url = 'http://bootstrap.saltstack.org'
            req = urllib2.urlopen(url)
            if req.getcode() != 200:
                self.error(
                    'Failed to download the latest stable version of the '
                    'bootstrap-salt.sh script from {0}. HTTP error: '
                    '{1}'.format(
                        url, req.getcode()
                    )
                )

            # Get the path to the built-in deploy scripts directory
            builtin_deploy_dir = os.path.join(
                os.path.dirname(__file__),
                'deploy'
            )

            # Compute the search path from the current loaded opts conf_file
            # value
            deploy_d_from_conf_file = os.path.join(
                os.path.dirname(self.config['conf_file']),
                'cloud.deploy.d'
            )

            # Compute the search path using the install time defined
            # syspaths.CONF_DIR
            deploy_d_from_syspaths = os.path.join(
                syspaths.CONFIG_DIR,
                'cloud.deploy.d'
            )

            # Get a copy of any defined search paths, flagging them not to
            # create parent
            deploy_scripts_search_paths = []
            for entry in self.config.get('deploy_scripts_search_path', []):
                if entry.startswith(builtin_deploy_dir):
                    # We won't write the updated script to the built-in deploy
                    # directory
                    continue

                if entry in (deploy_d_from_conf_file, deploy_d_from_syspaths):
                    # Allow parent directories to be made
                    deploy_scripts_search_paths.append((entry, True))
                else:
                    deploy_scripts_search_paths.append((entry, False))

            # In case the user is not using defaults and the computed
            # 'cloud.deploy.d' from conf_file and syspaths is not included, add
            # them
            if deploy_d_from_conf_file not in deploy_scripts_search_paths:
                deploy_scripts_search_paths.append(
                    (deploy_d_from_conf_file, True)
                )
            if deploy_d_from_syspaths not in deploy_scripts_search_paths:
                deploy_scripts_search_paths.append(
                    (deploy_d_from_syspaths, True)
                )

            for entry, makedirs in deploy_scripts_search_paths:
                if makedirs and not os.path.isdir(entry):
                    try:
                        os.makedirs(entry)
                    except (OSError, IOError) as err:
                        log.info(
                            'Failed to create directory {0!r}'.format(entry)
                        )
                        continue

                if not is_writeable(entry):
                    log.debug(
                        'The {0!r} is not writeable. Continuing...'.format(
                            entry
                        )
                    )
                    continue

                deploy_path = os.path.join(entry, 'bootstrap-salt.sh')
                try:
                    print(
                        '\nUpdating \'bootstrap-salt.sh\':'
                        '\n\tSource:      {0}'
                        '\n\tDestination: {1}'.format(
                            url,
                            deploy_path
                        )
                    )
                    with salt.utils.fopen(deploy_path, 'w') as fp_:
                        fp_.write(req.read())
                    # We were able to update, no need to continue trying to
                    # write up the search path
                    self.exit(0)
                except (OSError, IOError) as err:
                    log.debug(
                        'Failed to write the updated script: {0}'.format(err)
                    )
                    continue

            self.error('Failed to update the bootstrap script')

        log.info('salt-cloud starting')
        mapper = salt.cloud.Map(self.config)

        ret = {}

        if self.selected_query_option is not None:
            if self.selected_query_option == 'list_providers':
                try:
                    ret = mapper.provider_list()
                except (SaltCloudException, Exception) as exc:
                    msg = 'There was an error listing providers: {0}'
                    self.handle_exception(msg, exc)

            elif self.config.get('map', None):
                log.info('Applying map from {0!r}.'.format(self.config['map']))
                try:
                    ret = mapper.interpolated_map(
                        query=self.selected_query_option
                    )
                except (SaltCloudException, Exception) as exc:
                    msg = 'There was an error with a custom map: {0}'
                    self.handle_exception(msg, exc)
            else:
                try:
                    ret = mapper.map_providers_parallel(
                        query=self.selected_query_option
                    )
                except (SaltCloudException, Exception) as exc:
                    msg = 'There was an error with a map: {0}'
                    self.handle_exception(msg, exc)

        elif self.options.list_locations is not None:
            try:
                ret = mapper.location_list(
                    self.options.list_locations
                )
            except (SaltCloudException, Exception) as exc:
                msg = 'There was an error listing locations: {0}'
                self.handle_exception(msg, exc)

        elif self.options.list_images is not None:
            try:
                ret = mapper.image_list(
                    self.options.list_images
                )
            except (SaltCloudException, Exception) as exc:
                msg = 'There was an error listing images: {0}'
                self.handle_exception(msg, exc)

        elif self.options.list_sizes is not None:
            try:
                ret = mapper.size_list(
                    self.options.list_sizes
                )
            except (SaltCloudException, Exception) as exc:
                msg = 'There was an error listing sizes: {0}'
                self.handle_exception(msg, exc)

        elif self.options.destroy and (self.config.get('names', None) or
                                       self.config.get('map', None)):
            if self.config.get('map', None):
                log.info('Applying map from {0!r}.'.format(self.config['map']))
                matching = mapper.delete_map(query='list_nodes')
            else:
                matching = mapper.get_running_by_names(
                    self.config.get('names', ()),
                    profile=self.options.profile
                )

            if not matching:
                print('No machines were found to be destroyed')
                self.exit()

            msg = 'The following virtual machines are set to be destroyed:\n'
            names = set()
            for alias, drivers in matching.iteritems():
                msg += '  {0}:\n'.format(alias)
                for driver, vms in drivers.iteritems():
                    msg += '    {0}:\n'.format(driver)
                    for name in vms:
                        msg += '      {0}\n'.format(name)
                        names.add(name)
            try:
                if self.print_confirm(msg):
                    ret = mapper.destroy(names, cached=True)
            except (SaltCloudException, Exception) as exc:
                msg = 'There was an error destroying machines: {0}'
                self.handle_exception(msg, exc)

        elif self.options.action and (self.config.get('names', None) or
                                      self.config.get('map', None)):
            if self.config.get('map', None):
                log.info('Applying map from {0!r}.'.format(self.config['map']))
                names = mapper.get_vmnames_by_action(self.options.action)
            else:
                names = self.config.get('names', None)

            kwargs = {}
            machines = []
            msg = (
                'The following virtual machines are set to be actioned with '
                '"{0}":\n'.format(
                    self.options.action
                )
            )
            for name in names:
                if '=' in name:
                    # This is obviously not a machine name, treat it as a kwarg
                    comps = name.split('=')
                    kwargs[comps[0]] = comps[1]
                else:
                    msg += '  {0}\n'.format(name)
                    machines.append(name)
            names = machines

            try:
                if self.print_confirm(msg):
                    ret = mapper.do_action(names, kwargs)
            except (SaltCloudException, Exception) as exc:
                msg = 'There was an error actioning machines: {0}'
                self.handle_exception(msg, exc)

        elif self.options.function:
            kwargs = {}
            args = self.args[:]
            for arg in args[:]:
                if '=' in arg:
                    key, value = arg.split('=')
                    kwargs[key] = value
                    args.remove(arg)

            if args:
                self.error(
                    'Any arguments passed to --function need to be passed '
                    'as kwargs. Ex: image=ami-54cf5c3d. Remaining '
                    'arguments: {0}'.format(args)
                )
            try:
                ret = mapper.do_function(
                    self.function_provider, self.function_name, kwargs
                )
            except (SaltCloudException, Exception) as exc:
                msg = 'There was an error running the function: {0}'
                self.handle_exception(msg, exc)

        elif self.options.profile and self.config.get('names', False):
            try:
                ret = mapper.run_profile(
                    self.options.profile,
                    self.config.get('names')
                )
            except (SaltCloudException, Exception) as exc:
                msg = 'There was a profile error: {0}'
                self.handle_exception(msg, exc)

        elif self.config.get('map', None) and \
                self.selected_query_option is None:
            if len(mapper.rendered_map) == 0:
                sys.stderr.write('No nodes defined in this map')
                self.exit(1)
            try:
                ret = {}
                run_map = True

                log.info('Applying map from {0!r}.'.format(self.config['map']))
                dmap = mapper.map_data()

                msg = ''
                if 'errors' in dmap:
                    # display profile errors
                    msg += 'Found the following errors:\n'
                    for profile_name, error in dmap['errors'].iteritems():
                        msg += '  {0}: {1}\n'.format(profile_name, error)
                    sys.stderr.write(msg)
                    sys.stderr.flush()

                msg = ''
                if 'existing' in dmap:
                    msg += ('The following virtual machines were found '
                            'already running:\n')
                    for name in dmap['existing']:
                        msg += '  {0}\n'.format(name)

                if dmap['create']:
                    msg += ('The following virtual machines are set to be '
                            'created:\n')
                    for name in dmap['create']:
                        msg += '  {0}\n'.format(name)

                if 'destroy' in dmap:
                    msg += ('The following virtual machines are set to be '
                            'destroyed:\n')
                    for name in dmap['destroy']:
                        msg += '  {0}\n'.format(name)

                if not dmap['create'] and not dmap.get('destroy', None):
                    if not dmap.get('existing', None):
                        # nothing to create or destroy & nothing exists
                        print(msg)
                        self.exit(1)
                    else:
                        # nothing to create or destroy, print existing
                        run_map = False

                if run_map:
                    if self.print_confirm(msg):
                        ret = mapper.run_map(dmap)

                    if self.config.get('parallel', False) is False:
                        log.info('Complete')

                if dmap.get('existing', None):
                    for name in dmap['existing'].keys():
                        ret[name] = {'Message': 'Already running'}

            except (SaltCloudException, Exception) as exc:
                msg = 'There was a query error: {0}'
                self.handle_exception(msg, exc)

        else:
            self.error('Nothing was done. Using the proper arguments?')

        display_output = salt.output.get_printout(
            self.options.output, self.config
        )
        # display output using salt's outputter system
        print(display_output(ret))
        self.exit(0)

    def print_confirm(self, msg):
        if self.options.assume_yes:
            return True
        print(msg)
        res = raw_input('Proceed? [N/y] ')
        if not res.lower().startswith('y'):
            return False
        print('... proceeding')
        return True

    def handle_exception(self, msg, exc):
        if isinstance(exc, SaltCloudException):
            # It's a know exception an we know own to handle it
            if isinstance(exc, SaltCloudSystemExit):
                # This is a salt cloud system exit
                if exc.exit_code > 0:
                    # the exit code is bigger than 0, it's an error
                    msg = 'Error: {0}'.format(msg)
                self.exit(
                    exc.exit_code,
                    '{0}\n'.format(
                        msg.format(exc.message.rstrip())
                    )
                )
            # It's not a system exit but it's an error we can
            # handle
            self.error(
                msg.format(exc.message)
            )
        # This is a generic exception, log it, include traceback if
        # debug logging is enabled and exit.
        log.error(
            msg.format(exc),
            # Show the traceback if the debug logging level is
            # enabled
            exc_info=log.isEnabledFor(logging.DEBUG)
        )
        self.exit(1)
<commit_msg>Switch the update function to requests<commit_after># -*- coding: utf-8 -*-
'''
Primary interfaces for the salt-cloud system
'''
# Need to get data from 4 sources!
# CLI options
# salt cloud config - /etc/salt/cloud
# salt master config (for master integration)
# salt VM config, where VMs are defined - /etc/salt/cloud.profiles
#
# The cli, master and cloud configs will merge for opts
# the VM data will be in opts['profiles']

# Import python libs
from __future__ import print_function
import os
import sys
import getpass
import logging

# Import salt libs
import salt.config
import salt.output
import salt.utils
from salt import syspaths
from salt.utils import parsers
from salt.utils.validate.path import is_writeable
from salt.utils.verify import check_user, verify_env, verify_files

# Import salt.cloud libs
import salt.cloud
from salt.cloud.exceptions import SaltCloudException, SaltCloudSystemExit

log = logging.getLogger(__name__)

class SaltCloud(parsers.SaltCloudParser):

    def run(self):
        '''
        Execute the salt-cloud command line
        '''
        # Parse shell arguments
        self.parse_args()

        salt_master_user = self.config.get('user', getpass.getuser())
        if salt_master_user is not None and not check_user(salt_master_user):
            self.error(
                'salt-cloud needs to run as the same user as salt-master, '
                '{0!r}, but was unable to switch credentials. Please run '
                'salt-cloud as root or as {0!r}'.format(salt_master_user)
            )

        try:
            if self.config['verify_env']:
                verify_env(
                    [os.path.dirname(self.config['conf_file'])],
                    salt_master_user
                )
                logfile = self.config['log_file']
                if logfile is not None and not logfile.startswith('tcp://') \
                        and not logfile.startswith('udp://') \
                        and not logfile.startswith('file://'):
                    # Logfile is not using Syslog, verify
                    verify_files([logfile], salt_master_user)
        except (IOError, OSError) as err:
            log.error('Error while verifying the environment: {0}'.format(err))
            sys.exit(err.errno)

        # Setup log file logging
        self.setup_logfile_logger()

        if self.options.update_bootstrap:
            log.debug('Updating the bootstrap-salt.sh script to latest stable')
            try:
                import requests
            except ImportError:
                self.error (
                    'Updating the bootstrap-salt.sh script requires the '
                    'Python requests library to be installed'
                )
            url = 'http://bootstrap.saltstack.org'
            req = requests.get(url)
            if req.status_code != 200:
                self.error(
                    'Failed to download the latest stable version of the '
                    'bootstrap-salt.sh script from {0}. HTTP error: '
                    '{1}'.format(
                        url, req.getcode()
                    )
                )

            # Get the path to the built-in deploy scripts directory
            builtin_deploy_dir = os.path.join(
                os.path.dirname(__file__),
                'deploy'
            )

            # Compute the search path from the current loaded opts conf_file
            # value
            deploy_d_from_conf_file = os.path.join(
                os.path.dirname(self.config['conf_file']),
                'cloud.deploy.d'
            )

            # Compute the search path using the install time defined
            # syspaths.CONF_DIR
            deploy_d_from_syspaths = os.path.join(
                syspaths.CONFIG_DIR,
                'cloud.deploy.d'
            )

            # Get a copy of any defined search paths, flagging them not to
            # create parent
            deploy_scripts_search_paths = []
            for entry in self.config.get('deploy_scripts_search_path', []):
                if entry.startswith(builtin_deploy_dir):
                    # We won't write the updated script to the built-in deploy
                    # directory
                    continue

                if entry in (deploy_d_from_conf_file, deploy_d_from_syspaths):
                    # Allow parent directories to be made
                    deploy_scripts_search_paths.append((entry, True))
                else:
                    deploy_scripts_search_paths.append((entry, False))

            # In case the user is not using defaults and the computed
            # 'cloud.deploy.d' from conf_file and syspaths is not included, add
            # them
            if deploy_d_from_conf_file not in deploy_scripts_search_paths:
                deploy_scripts_search_paths.append(
                    (deploy_d_from_conf_file, True)
                )
            if deploy_d_from_syspaths not in deploy_scripts_search_paths:
                deploy_scripts_search_paths.append(
                    (deploy_d_from_syspaths, True)
                )

            for entry, makedirs in deploy_scripts_search_paths:
                if makedirs and not os.path.isdir(entry):
                    try:
                        os.makedirs(entry)
                    except (OSError, IOError) as err:
                        log.info(
                            'Failed to create directory {0!r}'.format(entry)
                        )
                        continue

                if not is_writeable(entry):
                    log.debug(
                        'The {0!r} is not writeable. Continuing...'.format(
                            entry
                        )
                    )
                    continue

                deploy_path = os.path.join(entry, 'bootstrap-salt.sh')
                try:
                    print(
                        '\nUpdating \'bootstrap-salt.sh\':'
                        '\n\tSource:      {0}'
                        '\n\tDestination: {1}'.format(
                            url,
                            deploy_path
                        )
                    )
                    with salt.utils.fopen(deploy_path, 'w') as fp_:
                        fp_.write(req.read())
                    # We were able to update, no need to continue trying to
                    # write up the search path
                    self.exit(0)
                except (OSError, IOError) as err:
                    log.debug(
                        'Failed to write the updated script: {0}'.format(err)
                    )
                    continue

            self.error('Failed to update the bootstrap script')

        log.info('salt-cloud starting')
        mapper = salt.cloud.Map(self.config)

        ret = {}

        if self.selected_query_option is not None:
            if self.selected_query_option == 'list_providers':
                try:
                    ret = mapper.provider_list()
                except (SaltCloudException, Exception) as exc:
                    msg = 'There was an error listing providers: {0}'
                    self.handle_exception(msg, exc)

            elif self.config.get('map', None):
                log.info('Applying map from {0!r}.'.format(self.config['map']))
                try:
                    ret = mapper.interpolated_map(
                        query=self.selected_query_option
                    )
                except (SaltCloudException, Exception) as exc:
                    msg = 'There was an error with a custom map: {0}'
                    self.handle_exception(msg, exc)
            else:
                try:
                    ret = mapper.map_providers_parallel(
                        query=self.selected_query_option
                    )
                except (SaltCloudException, Exception) as exc:
                    msg = 'There was an error with a map: {0}'
                    self.handle_exception(msg, exc)

        elif self.options.list_locations is not None:
            try:
                ret = mapper.location_list(
                    self.options.list_locations
                )
            except (SaltCloudException, Exception) as exc:
                msg = 'There was an error listing locations: {0}'
                self.handle_exception(msg, exc)

        elif self.options.list_images is not None:
            try:
                ret = mapper.image_list(
                    self.options.list_images
                )
            except (SaltCloudException, Exception) as exc:
                msg = 'There was an error listing images: {0}'
                self.handle_exception(msg, exc)

        elif self.options.list_sizes is not None:
            try:
                ret = mapper.size_list(
                    self.options.list_sizes
                )
            except (SaltCloudException, Exception) as exc:
                msg = 'There was an error listing sizes: {0}'
                self.handle_exception(msg, exc)

        elif self.options.destroy and (self.config.get('names', None) or
                                       self.config.get('map', None)):
            if self.config.get('map', None):
                log.info('Applying map from {0!r}.'.format(self.config['map']))
                matching = mapper.delete_map(query='list_nodes')
            else:
                matching = mapper.get_running_by_names(
                    self.config.get('names', ()),
                    profile=self.options.profile
                )

            if not matching:
                print('No machines were found to be destroyed')
                self.exit()

            msg = 'The following virtual machines are set to be destroyed:\n'
            names = set()
            for alias, drivers in matching.iteritems():
                msg += '  {0}:\n'.format(alias)
                for driver, vms in drivers.iteritems():
                    msg += '    {0}:\n'.format(driver)
                    for name in vms:
                        msg += '      {0}\n'.format(name)
                        names.add(name)
            try:
                if self.print_confirm(msg):
                    ret = mapper.destroy(names, cached=True)
            except (SaltCloudException, Exception) as exc:
                msg = 'There was an error destroying machines: {0}'
                self.handle_exception(msg, exc)

        elif self.options.action and (self.config.get('names', None) or
                                      self.config.get('map', None)):
            if self.config.get('map', None):
                log.info('Applying map from {0!r}.'.format(self.config['map']))
                names = mapper.get_vmnames_by_action(self.options.action)
            else:
                names = self.config.get('names', None)

            kwargs = {}
            machines = []
            msg = (
                'The following virtual machines are set to be actioned with '
                '"{0}":\n'.format(
                    self.options.action
                )
            )
            for name in names:
                if '=' in name:
                    # This is obviously not a machine name, treat it as a kwarg
                    comps = name.split('=')
                    kwargs[comps[0]] = comps[1]
                else:
                    msg += '  {0}\n'.format(name)
                    machines.append(name)
            names = machines

            try:
                if self.print_confirm(msg):
                    ret = mapper.do_action(names, kwargs)
            except (SaltCloudException, Exception) as exc:
                msg = 'There was an error actioning machines: {0}'
                self.handle_exception(msg, exc)

        elif self.options.function:
            kwargs = {}
            args = self.args[:]
            for arg in args[:]:
                if '=' in arg:
                    key, value = arg.split('=')
                    kwargs[key] = value
                    args.remove(arg)

            if args:
                self.error(
                    'Any arguments passed to --function need to be passed '
                    'as kwargs. Ex: image=ami-54cf5c3d. Remaining '
                    'arguments: {0}'.format(args)
                )
            try:
                ret = mapper.do_function(
                    self.function_provider, self.function_name, kwargs
                )
            except (SaltCloudException, Exception) as exc:
                msg = 'There was an error running the function: {0}'
                self.handle_exception(msg, exc)

        elif self.options.profile and self.config.get('names', False):
            try:
                ret = mapper.run_profile(
                    self.options.profile,
                    self.config.get('names')
                )
            except (SaltCloudException, Exception) as exc:
                msg = 'There was a profile error: {0}'
                self.handle_exception(msg, exc)

        elif self.config.get('map', None) and \
                self.selected_query_option is None:
            if len(mapper.rendered_map) == 0:
                sys.stderr.write('No nodes defined in this map')
                self.exit(1)
            try:
                ret = {}
                run_map = True

                log.info('Applying map from {0!r}.'.format(self.config['map']))
                dmap = mapper.map_data()

                msg = ''
                if 'errors' in dmap:
                    # display profile errors
                    msg += 'Found the following errors:\n'
                    for profile_name, error in dmap['errors'].iteritems():
                        msg += '  {0}: {1}\n'.format(profile_name, error)
                    sys.stderr.write(msg)
                    sys.stderr.flush()

                msg = ''
                if 'existing' in dmap:
                    msg += ('The following virtual machines were found '
                            'already running:\n')
                    for name in dmap['existing']:
                        msg += '  {0}\n'.format(name)

                if dmap['create']:
                    msg += ('The following virtual machines are set to be '
                            'created:\n')
                    for name in dmap['create']:
                        msg += '  {0}\n'.format(name)

                if 'destroy' in dmap:
                    msg += ('The following virtual machines are set to be '
                            'destroyed:\n')
                    for name in dmap['destroy']:
                        msg += '  {0}\n'.format(name)

                if not dmap['create'] and not dmap.get('destroy', None):
                    if not dmap.get('existing', None):
                        # nothing to create or destroy & nothing exists
                        print(msg)
                        self.exit(1)
                    else:
                        # nothing to create or destroy, print existing
                        run_map = False

                if run_map:
                    if self.print_confirm(msg):
                        ret = mapper.run_map(dmap)

                    if self.config.get('parallel', False) is False:
                        log.info('Complete')

                if dmap.get('existing', None):
                    for name in dmap['existing'].keys():
                        ret[name] = {'Message': 'Already running'}

            except (SaltCloudException, Exception) as exc:
                msg = 'There was a query error: {0}'
                self.handle_exception(msg, exc)

        else:
            self.error('Nothing was done. Using the proper arguments?')

        display_output = salt.output.get_printout(
            self.options.output, self.config
        )
        # display output using salt's outputter system
        print(display_output(ret))
        self.exit(0)

    def print_confirm(self, msg):
        if self.options.assume_yes:
            return True
        print(msg)
        res = raw_input('Proceed? [N/y] ')
        if not res.lower().startswith('y'):
            return False
        print('... proceeding')
        return True

    def handle_exception(self, msg, exc):
        if isinstance(exc, SaltCloudException):
            # It's a know exception an we know own to handle it
            if isinstance(exc, SaltCloudSystemExit):
                # This is a salt cloud system exit
                if exc.exit_code > 0:
                    # the exit code is bigger than 0, it's an error
                    msg = 'Error: {0}'.format(msg)
                self.exit(
                    exc.exit_code,
                    '{0}\n'.format(
                        msg.format(exc.message.rstrip())
                    )
                )
            # It's not a system exit but it's an error we can
            # handle
            self.error(
                msg.format(exc.message)
            )
        # This is a generic exception, log it, include traceback if
        # debug logging is enabled and exit.
        log.error(
            msg.format(exc),
            # Show the traceback if the debug logging level is
            # enabled
            exc_info=log.isEnabledFor(logging.DEBUG)
        )
        self.exit(1)
<|endoftext|>


<commit_before>#!/usr/bin/env python3

# Imports
import pyglet ## INSTALL
from pyglet.gl import *
from pyglet.window import mouse
from pyglet.window import key
from enum import Enum
from queue import LifoQueue, Queue, PriorityQueue
import sys
import numpy as np ## INSTALL
from copy import deepcopy
from random import randint
import random
from timeit import default_timer as timer

# Class

def MyPriorityQueue(PriorityQueue):
    def get(self):
        return super().get()[-1]

class Dim(Enum):
	x = 4
	y = 4

# Globals
global maxdimension
global puzzle
global solution
global dimensions
global steps
global solved
global limit

# Performance Data
added_nodes = 0
closed_nodes = 0
omitted_nodes = 0
heuristic_calls = 0

# global values
solved = True
limit = 15  # maximum heuristicCost of new random Puzzle ( <= len(solution))
steps = 20  # number of steps to shuffle puzzle ( >= len(solution))

# ui
font_large = 32
font_small = 14
font_number = 20

dimensions = (Dim.x.value,Dim.y.value)

drawsize = 1
blankvalue = 0

# Functions

def printPuzzle(puzzle):
    for y in range(Dim.y.value):
        line = ""
        for x in range(Dim.x.value):
            line = line + " " + str(puzzle[y][x])
        print(line)
    print("-----")

def shufflePuzzle(puzzle, iterations):
    lastdirection = 0
    directions = ["left","right","up","down"]
    direction = 0
    for i in range(iterations):
        while direction == lastdirection:
                direction = directions[randint(0,len(directions)-1)]
        lastdirection = direction
        puzzle = moveblank(puzzle, direction)
    return puzzle

def getPosition(puzzle, number):
    numberindex = []
    for y in range(Dim.y.value):
        for x in range(Dim.x.value):
            if puzzle[y][x] == number:
                #print(str(x) + str(y) + str(puzzle[y][x]) + str(number))
                return (x,y)
    return numberindex

def heuristicCost(path):
    puzzle = path[-1]
    cost = 0
    for y in range(dimensions[1]):
        for x in range(dimensions[0]):
            expectednumber = y*Dim.x.value+x+1
            if expectednumber == Dim.x.value*Dim.y.value:
                #expectednumber = 0
                continue
            actualposition = getPosition(puzzle,expectednumber)
            manhattanDist = abs(x - actualposition[0]) + abs(y - actualposition[1])
            cost += manhattanDist
            #print("expected: " + str(expectednumber))
            #print("pos: " + str(x) + str(y) + " pos of exp: " + str(actualposition[0]) + str(actualposition[1]))
            #print("cumulated cost: " + str(cost))
            #Linear Conflict for columns (x): add 2 for each conflict
            if y == 0 and manhattanDist > 0:

                cost += 0
        #Linear Conflict for rows (y): add 2 for each conflict
        cost += 0

    global heuristic_calls
    heuristic_calls += 1
    return len(path)-1 + cost

def isSolveAble(puzzle):
    plist = []
    #printPuzzle(puzzle)
    for y in range(Dim.y.value):
        for x in range(Dim.x.value):
            plist.append(puzzle[y][x])
    inversions = 0
    plist.remove(0)
    for num in plist:
        for other in plist:
            if (plist.index(num) < plist.index(other)) and (other < num):
                inversions += 1
    blankrow = getPosition(puzzle,0)[1]
    print("blankrow: " + str(blankrow))
    print("inversions: " + str(inversions))
    if (Dim.x.value%2) != 0:
        return (inversions%2) == 0
    else:
        if ((Dim.y.value - blankrow)%2) != 0:
            return (inversions%2) == 0
        else:
            return (inversions%2) != 0

def getRandomPuzzle():
    while True:
        numbers = list(range(Dim.x.value*Dim.y.value))
        randomPuzzle = {}
        for y in range(Dim.y.value):
            randomPuzzle[y] = {}
            for x in range(Dim.x.value):
                num = random.choice(numbers)
                randomPuzzle[y][x] = num
                numbers.remove(num)
        if isSolveAble(randomPuzzle) and (heuristicCost([randomPuzzle])<limit):
            print("heuristicCost: " + str(heuristicCost([randomPuzzle])))
            return randomPuzzle

def updatePuzzle(puzzle, clickedNumber):
    puzzle = deepcopy(puzzle)
    zeroindex = getPosition(puzzle, blankvalue)
    clickedindex = getPosition(puzzle, clickedNumber)

    if clickedindex[0] == zeroindex[0]:
        diff = abs(clickedindex[1] - zeroindex[1])
        if diff <= 1:
                puzzle[clickedindex[1]][clickedindex[0]] = blankvalue
                puzzle[zeroindex[1]][zeroindex[0]] = clickedNumber
    if clickedindex[1] == zeroindex[1]:
        diff = abs(clickedindex[0] - zeroindex[0])
        if diff <= 1:
                puzzle[clickedindex[1]][clickedindex[0]] = blankvalue
                puzzle[zeroindex[1]][zeroindex[0]] = clickedNumber
    return puzzle

def moveblank(puzzle, direction):
    adjacentnumber = 0
    changed = False
    zeroindex = getPosition(puzzle, blankvalue)
    if direction == "left":
        if zeroindex[0] > 0:
            adjacentnumber = puzzle[zeroindex[1]][zeroindex[0]-1]
            changed = True
    elif direction == "right":
        if zeroindex[0] < dimensions[0]-1:
            adjacentnumber = puzzle[zeroindex[1]][zeroindex[0]+1]
            changed = True
    elif direction == "up":
        if zeroindex[1] < dimensions[1]-1:
            adjacentnumber = puzzle[zeroindex[1]+1][zeroindex[0]]
            changed = True
    elif direction == "down":
        if zeroindex[1] > 0:
            adjacentnumber = puzzle[zeroindex[1]-1][zeroindex[0]]
            changed = True
    if changed:
        return updatePuzzle(puzzle, adjacentnumber)
    else:
        return puzzle

def switchTile(puzzle,position):
    #TODO implement
    return puzzle

def getNeighbors(puzzle):
    #TODO: optimize check, currently very slow
    #TODO: use switchTile instead of moveblank
    neighborTable = []
    left, right, up, down = (moveblank(puzzle,"left"), moveblank(puzzle,"right"),
        moveblank(puzzle,"up"), moveblank(puzzle,"down"))
    if left != puzzle:
        neighborTable.append(left)
    if right != puzzle:
        neighborTable.append(right)
    if up != puzzle:
        neighborTable.append(up)
    if down != puzzle:
        neighborTable.append(down)
    return neighborTable

def genericSearch(startPosList, endPosList, _dataStructure=Queue, _heuristic=False, _debug=False):
    visited = []
    frontier = _dataStructure()
    max_frontier_len = 0
    ####
    global added_notes
    added_nodes = 0
    global heuristic_calls
    heuristic_calls = 0
    ####
    for startPos in startPosList:
        if _heuristic:
            frontier.put((0,[startPos]))
        else:
            frontier.put([startPos])

    while not frontier.empty():
        if frontier.qsize() > max_frontier_len: max_frontier_len = frontier.qsize()
        path = []
        if _heuristic:
            path = frontier.get()[-1]
        else:
            path = frontier.get()

        head = path[-1]

        if head not in visited:
            visited.append(head)
            ####
            if len(visited) % 1000 == 0:
                print("Heuristic calls: " + str(heuristic_calls))
                print("Visited nodes: " + str(len(visited)))
                print("Max frontier: " + str(max_frontier_len))
            ####
            if head in endPosList:
                return path,len(visited),max_frontier_len
            for neighbor in getNeighbors(head):
                new_path = [n for n in path]
                new_path.append(neighbor)
                if _debug:
                    debug(space,new_path)
                if _heuristic:
                    #####
                    heuristic_calls += 1
                    #####
                    frontier.put((heuristicCost(new_path),id(new_path),new_path))
                else:
                    frontier.put(new_path)
    return 0,len(visited),max_frontier_len

def initPuzzle():
    # Init puzzle
    puzzle = {}
    for i in range(Dim.y.value):
        puzzle[i] = {}
        for j in range(Dim.x.value):
            puzzle[i][j] = (j+1+Dim.x.value*i)

    puzzle[Dim.y.value-1][Dim.x.value-1] = blankvalue
    return puzzle

def getHint(puzzle):
    #TODO: print or display real hint instead of puzzle
    global solution
    if puzzle == endpuzzle:
        print("hint: do nothing")
    else:
        print("calculating hint ...")
        solution = genericSearch([puzzle],[endpuzzle])[0]
        if len(solution) != 0:
            printPuzzle(solution[1])

#pyglet init
window = pyglet.window.Window(resizable=True, caption='15-Puzzle')

maxdimension = min(window.width,window.height)

bgimg = None
#bgimg = pyglet.resource.image('data/img/img.png')

pyglet.gl.glClearColor(0.1,0.1,0.1,1)

puzzle = initPuzzle()

endpuzzle = deepcopy(puzzle)

solution = []

    #puzzle = shufflePuzzle(puzzle,steps)
    #printPuzzle(puzzle)
    #printPuzzle(endpuzzle)

    #solution = genericSearch([puzzle],[endpuzzle])[0]

    #printPuzzle(puzzle)

@window.event
def on_resize(width, height):
    global maxdimension
    maxdimension = min(width,height)
    print('The window was resized to %dx%d' % (width, height))
    if bgimg != None:
        bgimg.width = maxdimension
        bgimg.height = maxdimension

@window.event
def on_draw():
    window.clear()

    global puzzle
    global endpuzzle
    global solved

    if puzzle == endpuzzle:
        solved = True

    offsetx, offsety = ((window.width-maxdimension)/2, (window.height-maxdimension)/2)

    if bgimg != None:
        bgimg.blit(offsetx,offsety)

    for i in range(Dim.y.value):
        for j in range(Dim.x.value):
            if puzzle[i][j] != 0:
                number = pyglet.text.Label(str(puzzle[i][j]), font_size=font_number, x=offsetx+(j+1)*(maxdimension/(Dim.x.value+1)), y=offsety+(i+1)*(maxdimension/(Dim.y.value+1)),
                    anchor_x='center', anchor_y='center')
                number.draw()

    if solved:
        labelstring = "SOLVED"
    else:
        labelstring = ''

    label = pyglet.text.Label(labelstring, font_name='Times New Roman', font_size=font_large,
                x=window.width//2, y=window.height-36, anchor_x='center', anchor_y='center')
    label.draw()

    controls = ["Arrowkeys to move tiles",
                "'b' - shuffle and BFS",
                "'a' - randomize and A*",
                "'ENTER' - go to solution",
                "'SPACE' - step through solution",
                "'c' - display current heuristic cost",
                "'h' - get a hint for next move"]
    for i in range(len(controls)):
        pyglet.text.Label(controls[i], font_name='Times New Roman', font_size=font_small,
                x=16, y=(len(controls)+1-i)*round(1.5*font_small), anchor_x='left', anchor_y='center').draw()
    solved = False

@window.event
def on_key_press(symbol, modifiers):
    global solution
    global puzzle
    global steps

    if symbol == key.B:     # randomize puzzle
        if len(solution) != 0:
            solution = []
        puzzle = deepcopy(endpuzzle)
        puzzle = shufflePuzzle(puzzle,steps)
        print("Built puzzle with",steps,"random moves.")
        print("searching...")
        tstart = timer()
        solution = genericSearch([puzzle],[endpuzzle])[0]
        tend = timer()
        elapsed_time = tend - tstart
        print("search complete, number of steps: ",len(solution)-1,
            ". time to complete: ", elapsed_time, "s.")
        print("Is solveable: " + str(isSolveAble(puzzle)))
    elif symbol == key.A:
        if len(solution) != 0:
            puzzle = solution.pop()
            solution = []
        #puzzle = shufflePuzzle(puzzle,steps)
        puzzle = getRandomPuzzle()
        print("Built complete random puzzle")
        print("searching...")
        tstart = timer()
        solution = genericSearch([puzzle],[endpuzzle],_dataStructure=PriorityQueue,_heuristic=True)[0]
        tend = timer()
        elapsed_time = tend - tstart
        print("search complete, number of steps: ",len(solution)-1,
            ". time to complete: ", elapsed_time, "s.")
    elif symbol == key.ENTER:   # step to solution
        if len(solution) != 0:
            solution = []
        puzzle = deepcopy(endpuzzle)
    elif symbol == key.SPACE:
        if len(solution) != 0:
            puzzle = solution.pop(0)
        else:
            print("done")
    elif symbol == key.C:
        print("Absolute cost: " + str(heuristicCost([puzzle])))
    elif symbol == key.H:
        getHint(puzzle)
    elif symbol == key.LEFT:
        puzzle = moveblank(puzzle, "right")
    elif symbol == key.RIGHT:
        puzzle = moveblank(puzzle, "left")
    elif symbol == key.UP:
        puzzle = moveblank(puzzle, "down")
    elif symbol == key.DOWN:
        puzzle = moveblank(puzzle, "up")
@window.event
def on_mouse_press(x, y, button, modifiers):
    global puzzle
    #if button == mouse.LEFT:

pyglet.app.run()

if __name__ == '__main__':
    if False:
        cProfile.run('main()')
    else:
        main()<commit_msg>Minor changes<commit_after>#!/usr/bin/env python3

# Imports
import pyglet # INSTALL
import pyglet.gl
from pyglet.window import mouse # mouse unused
from pyglet.window import key
from enum import Enum
from queue import LifoQueue, Queue, PriorityQueue # LifoQueue unused
import sys # unused
import numpy as np  # INSTALL # unused
from copy import deepcopy
from random import randint
import random
from timeit import default_timer as timer

# Class

def MyPriorityQueue(PriorityQueue):
    def get(self):
        return super().get()[-1]

class Dim(Enum):
    x = 4
    y = 4

# Globals
global maxdimension
global puzzle
global solutions
global dimensions
global steps
global solved
global limit

# Performance Data
added_nodes = 0
closed_nodes = 0
omitted_nodes = 0
heuristic_calls = 0

# global values
solved = True
limit = 15  # maximum heuristicCost of new random Puzzle ( <= len(solution))
steps = 20  # number of steps to shuffle puzzle ( >= len(solution))

# ui
font_large = 32
font_small = 14
font_number = 20

dimensions = (Dim.x.value, Dim.y.value)

drawsize = 1
blankvalue = 0

# Functions

def printPuzzle(puzzle):
    for y in range(Dim.y.value):
        line = ""
        for x in range(Dim.x.value):
            line = line + " " + str(puzzle[y][x])
        print(line)
    print("-----")

def shufflePuzzle(puzzle, iterations):
    lastdirection = 0
    directions = ["left", "right", "up", "down"]
    direction = 0
    for i in range(iterations):
        while direction == lastdirection:
                direction = directions[randint(0, len(directions)-1)]
        lastdirection = direction
        puzzle = moveblank(puzzle, direction)
    return puzzle

def getPosition(puzzle, number):
    numberindex = []
    for y in range(Dim.y.value):
        for x in range(Dim.x.value):
            if puzzle[y][x] == number:
                # print(str(x) + str(y) + str(puzzle[y][x]) + str(number))
                return (x, y)
    return numberindex

def heuristicCost(path):
    puzzle = path[-1]
    cost = 0
    for y in range(dimensions[1]):
        for x in range(dimensions[0]):
            expectednumber = y*Dim.x.value+x+1
            if expectednumber == Dim.x.value*Dim.y.value:
                # expectednumber = 0
                continue
            actualposition = getPosition(puzzle, expectednumber)
            manhattanDist = abs(x - actualposition[0])\
                + abs(y - actualposition[1])
            cost += manhattanDist
            # print("expected: " + str(expectednumber))
            # print("pos: " + str(x) + str(y) + " pos of exp: " + str(actualposition[0]) + str(actualposition[1]))
            # print("cumulated cost: " + str(cost))
            # Linear Conflict for columns (x): add 2 for each conflict
            if y == 0 and manhattanDist > 0:

                cost += 0
        # Linear Conflict for rows (y): add 2 for each conflict
        cost += 0

    global heuristic_calls
    heuristic_calls += 1
    return len(path)-1 + cost

def isSolveAble(puzzle):
    plist = []
    # printPuzzle(puzzle)
    for y in range(Dim.y.value):
        for x in range(Dim.x.value):
            plist.append(puzzle[y][x])
    inversions = 0
    plist.remove(0)
    for num in plist:
        for other in plist:
            if (plist.index(num) < plist.index(other)) and (other < num):
                inversions += 1
    blankrow = getPosition(puzzle, 0)[1]
    print("blankrow: " + str(blankrow))
    print("inversions: " + str(inversions))
    if (Dim.x.value % 2) != 0:
        return (inversions % 2) == 0
    else:
        if ((Dim.y.value - blankrow) % 2) != 0:
            return (inversions % 2) == 0
        else:
            return (inversions % 2) != 0

def getRandomPuzzle():
    while True:
        numbers = list(range(Dim.x.value*Dim.y.value))
        rndPuzzle = {}
        for y in range(Dim.y.value):
            rndPuzzle[y] = {}
            for x in range(Dim.x.value):
                num = random.choice(numbers)
                rndPuzzle[y][x] = num
                numbers.remove(num)
        if isSolveAble(rndPuzzle) and (heuristicCost([rndPuzzle]) < limit):
            print("heuristicCost: " + str(heuristicCost([rndPuzzle])))
            return rndPuzzle

def updatePuzzle(puzzle, clickedNumber):
    puzzle = deepcopy(puzzle)
    zeroindex = getPosition(puzzle, blankvalue)
    clickedindex = getPosition(puzzle, clickedNumber)

    if clickedindex[0] == zeroindex[0]:
        diff = abs(clickedindex[1] - zeroindex[1])
        if diff <= 1:
                puzzle[clickedindex[1]][clickedindex[0]] = blankvalue
                puzzle[zeroindex[1]][zeroindex[0]] = clickedNumber
    if clickedindex[1] == zeroindex[1]:
        diff = abs(clickedindex[0] - zeroindex[0])
        if diff <= 1:
                puzzle[clickedindex[1]][clickedindex[0]] = blankvalue
                puzzle[zeroindex[1]][zeroindex[0]] = clickedNumber
    return puzzle

def moveblank(puzzle, direction):
    adjacentnumber = 0
    changed = False
    zeroindex = getPosition(puzzle, blankvalue)
    if direction == "left":
        if zeroindex[0] > 0:
            adjacentnumber = puzzle[zeroindex[1]][zeroindex[0]-1]
            changed = True
    elif direction == "right":
        if zeroindex[0] < dimensions[0]-1:
            adjacentnumber = puzzle[zeroindex[1]][zeroindex[0]+1]
            changed = True
    elif direction == "up":
        if zeroindex[1] < dimensions[1]-1:
            adjacentnumber = puzzle[zeroindex[1]+1][zeroindex[0]]
            changed = True
    elif direction == "down":
        if zeroindex[1] > 0:
            adjacentnumber = puzzle[zeroindex[1]-1][zeroindex[0]]
            changed = True
    if changed:
        return updatePuzzle(puzzle, adjacentnumber)
    else:
        return puzzle

def switchTile(puzzle, position):
    # TODO implement
    return puzzle

def getNeighbors(puzzle):
    # TODO: optimize check, currently very slow
    # TODO: use switchTile instead of moveblank
    neighborTable = []
    left, right, up, down = (moveblank(puzzle, "left"),
                             moveblank(puzzle, "right"),
                             moveblank(puzzle, "up"),
                             moveblank(puzzle, "down"))
    if left != puzzle:
        neighborTable.append(left)
    if right != puzzle:
        neighborTable.append(right)
    if up != puzzle:
        neighborTable.append(up)
    if down != puzzle:
        neighborTable.append(down)
    return neighborTable

def genericSearch(startPosList, endPosList, _dataStructure=Queue,
                  _heuristic=False, _debug=False):
    visited = []
    frontier = _dataStructure()
    max_frontier_len = 0
    ####
    global added_notes
    added_nodes = 0 # Never used
    global heuristic_calls
    heuristic_calls = 0
    ####
    for startPos in startPosList:
        if _heuristic:
            frontier.put((0, [startPos]))
        else:
            frontier.put([startPos])

    while not frontier.empty():
        if frontier.qsize() > max_frontier_len:
            max_frontier_len = frontier.qsize()
        path = []
        if _heuristic:
            path = frontier.get()[-1]
        else:
            path = frontier.get()

        head = path[-1]

        if head not in visited:
            visited.append(head)
            ####
            if len(visited) % 1000 == 0:
                print("Heuristic calls: " + str(heuristic_calls))
                print("Visited nodes: " + str(len(visited)))
                print("Max frontier: " + str(max_frontier_len))
            ####
            if head in endPosList:
                return path, len(visited), max_frontier_len

            for neighbor in getNeighbors(head):
                new_path = [n for n in path]
                new_path.append(neighbor)
                if _debug:
                    debug(space, new_path)
                if _heuristic:
                    #####
                    heuristic_calls += 1
                    #####
                    frontier.put((heuristicCost(new_path),
                                  id(new_path), new_path))
                else:
                    frontier.put(new_path)
    return 0, len(visited), max_frontier_len

def initPuzzle():
    # Init puzzle
    puzzle = {}
    for i in range(Dim.y.value):
        puzzle[i] = {}
        for j in range(Dim.x.value):
            puzzle[i][j] = (j+1+Dim.x.value*i)

    puzzle[Dim.y.value-1][Dim.x.value-1] = blankvalue
    return puzzle

def getHint(puzzle):
    # TODO: print or display real hint instead of puzzle
    global solution
    if puzzle == endpuzzle:
        print("hint: do nothing")
    else:
        print("calculating hint ...")
        solution = genericSearch([puzzle], [endpuzzle])[0]
        if len(solution) != 0:
            printPuzzle(solution[1])

# pyglet init
window = pyglet.window.Window(resizable=True, caption='15-Puzzle')

maxdimension = min(window.width, window.height)

bgimg = None
# bgimg = pyglet.resource.image('data/img/img.png')

pyglet.gl.glClearColor(0.1, 0.1, 0.1, 1)

puzzle = initPuzzle()

endpuzzle = deepcopy(puzzle)

solution = []

# puzzle = shufflePuzzle(puzzle,steps)
# printPuzzle(puzzle)
# printPuzzle(endpuzzle)

# solution = genericSearch([puzzle],[endpuzzle])[0]

# printPuzzle(puzzle)

@window.event
def on_resize(width, height):
    global maxdimension
    maxdimension = min(width, height)
    print('The window was resized to %dx%d' % (width, height))
    if bgimg is not None:
        bgimg.width = maxdimension
        bgimg.height = maxdimension

@window.event
def on_draw():
    window.clear()

    global puzzle
    global endpuzzle
    global solved

    if puzzle == endpuzzle:
        solved = True

    offsetx, offsety = ((window.width-maxdimension)/2,
                        (window.height-maxdimension)/2)

    if bgimg is not None:
        bgimg.blit(offsetx, offsety)

    for i in range(Dim.y.value):
        for j in range(Dim.x.value):
            if puzzle[i][j] != 0:
                number = pyglet.text.Label(
                    str(puzzle[i][j]), font_size=font_number,
                    x=offsetx+(j+1)*(maxdimension/(Dim.x.value+1)),
                    y=offsety+(i+1)*(maxdimension/(Dim.y.value+1)),
                    anchor_x='center', anchor_y='center')
                number.draw()

    if solved:
        labelstring = "SOLVED"
    else:
        labelstring = ''

    label = pyglet.text.Label(labelstring, font_name='Times New Roman',
                              font_size=font_large, x=window.width//2,
                              y=window.height-36, anchor_x='center',
                              anchor_y='center')
    label.draw()

    controls = ["Arrowkeys to move tiles",
                "'b' - shuffle and BFS",
                "'a' - randomize and A*",
                "'ENTER' - go to solution",
                "'SPACE' - step through solution",
                "'c' - display current heuristic cost",
                "'h' - get a hint for next move"]
    for i in range(len(controls)):
        pyglet.text.Label(controls[i], font_name='Times New Roman',
                          font_size=font_small, x=16,
                          y=(len(controls)+1-i)*round(1.5*font_small),
                          anchor_x='left', anchor_y='center').draw()
    solved = False

@window.event
def on_key_press(symbol, modifiers):
    global solution
    global puzzle
    global steps

    if symbol == key.B:     # randomize puzzle
        if len(solution) != 0:
            solution = []
        puzzle = deepcopy(endpuzzle)
        puzzle = shufflePuzzle(puzzle, steps)
        print("Built puzzle with", steps, "random moves.")
        print("searching...")
        tstart = timer()
        solution = genericSearch([puzzle], [endpuzzle])[0]
        tend = timer()
        elapsed_time = tend - tstart
        print("search complete, number of steps: ", len(solution)-1,
              ". time to complete: ", elapsed_time, "s.")
        print("Is solveable: " + str(isSolveAble(puzzle)))
    elif symbol == key.A:
        if len(solution) != 0:
            puzzle = solution.pop()
            solution = []
        # puzzle = shufflePuzzle(puzzle,steps)
        puzzle = getRandomPuzzle()
        print("Built complete random puzzle")
        print("searching...")
        tstart = timer()
        solution = genericSearch([puzzle], [endpuzzle],
                                 _dataStructure=PriorityQueue,
                                 _heuristic=True)[0]
        tend = timer()
        elapsed_time = tend - tstart
        print("search complete, number of steps: ", len(solution)-1,
              ". time to complete: ", elapsed_time, "s.")
    elif symbol == key.ENTER:   # step to solution
        if len(solution) != 0:
            solution = []
        puzzle = deepcopy(endpuzzle)
    elif symbol == key.SPACE:
        if len(solution) != 0:
            puzzle = solution.pop(0)
        else:
            print("done")
    elif symbol == key.C:
        print("Absolute cost: " + str(heuristicCost([puzzle])))
    elif symbol == key.H:
        getHint(puzzle)
    elif symbol == key.LEFT:
        puzzle = moveblank(puzzle, "right")
    elif symbol == key.RIGHT:
        puzzle = moveblank(puzzle, "left")
    elif symbol == key.UP:
        puzzle = moveblank(puzzle, "down")
    elif symbol == key.DOWN:
        puzzle = moveblank(puzzle, "up")

@window.event
def on_mouse_press(x, y, button, modifiers):
    global puzzle
    # if button == mouse.LEFT:

if __name__ == '__main__':
    pyglet.app.run()
<|endoftext|>