How does super()._init_ work in pygame? - pygame

I would just like a general explanation of how to use a class and subclass in pygame?

Keep snake as list of its segments/elements. Every element keeps segment position (x,y). First element is snake head. ie.
snake = [(10,10), (11,10), (12,10)]
When snake moves then you append new position of head at the beginning of list and you remove last element - and snake/list have the same length.
# new head position
snake.insert(0, (9,10))
# remove tail
snake.pop()
When snake eats something then you don't remove last element from list. This way snake is longer.
# init data
make_longer = False
...
# snake eat sothing
make_longer = True
...
# new head position
snake.insert(0, (9,10))
# remove tail
if make_longer:
# skip removing last element once
make_longer = False
else:
snake.pop()
If you want to make longer more than one element then don't remove last element more than once.
# init data
make_longer = 0
...
# snake eat sothing
# make_longer = 3
make_longer += 3 # better when snake eats more apples in short time
...
# new head position
snake.insert(0, (9,10))
# remove tail
if make_longer > 0:
make_longer -= 1
else:
snake.pop()

Related

Plotly Express: Prevent bars from stacking when Y-axis catgories have the same name

I'm new to plotly.
Working with:
Ubuntu 20.04
Python 3.8.10
plotly==5.10.0
I'm doing a comparative graph using a horizontal bar chart. Different instruments measuring the same chemical compounds. I want to be able to do an at-a-glance, head-to-head comparison if the measured value amongst all machines.
The problem is; if the compound has the same name amongst the different instruments - Plotly stacks the data bars into a single bar with segment markers. I very much want each bar to appear individually. Is there a way to prevent Plotly Express from automatically stacking the common bars??
Examples:
CODE
gobardata = []
for blended_name in _df[:20].blended_name: # should always be unique
##################################
# Unaltered compound names
compound_names = [str(c) for c in _df[_df.blended_name == blended_name]["injcompound_name"].tolist()]
# Random number added to end of compound_names to make every string unique
# compound_names = ["{} ({})".format(str(c),random.randint(0, 1000)) for c in _df[_df.blended_name == blended_name]["injcompound_name"].tolist()]
##################################
deltas = _df[_df.blended_name == blended_name]["delta_rettime"].to_list()
gobardata.append(
go.Bar(
name = blended_name,
x = deltas,
y = compound_names,
orientation='h',
))
fig = go.Figure(data = gobardata)
fig.update_traces(width=1)
fig.update_layout(
bargap=1,
bargroupgap=.1,
xaxis_title="Delta Retention Time (Expected - actual)",
yaxis_title="Instrument name(Injection ID)"
)
fig.show()
What I'm getting (Using actual, but repeated, compound names)
What I want (Adding random text to each compound name to make it unique)
OK. Figured it out. This is probably pretty klugy, but it consistently works.
Basically...
Use go.FigureWidget...
...with make_subplots having a common x-axis...
...controlling the height of each subplot based on number of bars.
Every bar in each subplot is added as an individual trace...
...using a dictionary matching bar name to a common color.
The y-axis labels for each subplot is a list containing the machine name as [0], and then blank placeholders ('') so the length of the y-axis list matches the number of bars.
And manually manipulating the legend so each bar name appears only once.
# Get lists of total data
all_compounds = list(_df.injcompound_name.unique())
blended_names = list(_df.blended_name.unique())
#################################################################
# The heights of each subplot have to be set when fig is created.
# fig has to be created before adding traces.
# So, create a list of dfs, and use these to calculate the subplot heights
dfs = []
subplot_height_multiplier = 20
subplot_heights = []
for blended_name in blended_names:
df = _df[(_df.blended_name == blended_name)]#[["delta_rettime", "injcompound_name"]]
dfs.append(df)
subplot_heights.append(df.shape[0] * subplot_height_multiplier)
chart_height = sum(subplot_heights) # Prep for the height of the overall chart.
chart_width = 1000
# Make the figure
fig = make_subplots(
rows=len(blended_names),
cols=1,
row_heights = subplot_heights,
shared_xaxes=True,
)
# Create the color dictionary to match a color to each compound
_CSS_color = CSS_chart_color_list()
colors = {}
for compound in all_compounds:
try: colors[compound] = _CSS_color.pop()
except IndexError:
# Probably ran out of colors, so just reuse
_CSS_color = CSS_color.copy()
colors[compound] = _CSS_color.pop()
rowcount = 1
for df in dfs:
# Add bars individually to each subplot
bars = []
for label, labeldf in df.groupby('injcompound_name'):
fig.add_trace(
go.Bar(x = labeldf.delta_rettime,
y = [labeldf.blended_name.iloc[0]]+[""]*(len(labeldf.delta_rettime)-1),
name = label,
marker = {'color': colors[label]},
orientation = 'h',
),
row=rowcount,
col=1,
)
rowcount += 1
# Set figure to FigureWidget
fig = go.FigureWidget(fig)
# Adding individual traces creates redundancies in the legend.
# This removes redundancies from the legend
names = set()
fig.for_each_trace(
lambda trace:
trace.update(showlegend=False)
if (trace.name in names) else names.add(trace.name))
fig.update_layout(
height=chart_height,
width=chart_width,
title_text="∆ of observed RT to expected RT",
showlegend = True,
)
fig.show()

training CNN model using word2vectorization,while invoking get_vector() .Showing error " KeyError: 'CALLDATASIZE' " while preparing train_x

set of word vectors are generated from github link:https://github.com/jianwei76/SoliAudit/blob/master/va/features/op.origin.csv.xz.
Converted this op.origin.csv.xz file to .txt file using gen_doc() function,
opfile=op.origin.csv.xz #downloaded and uploaded in google colab folder
binfile=model.bin # new binfile created to save the model generated from word2vec model
def op_name(op):
return op.rstrip('0123456789')
def filter_op(op_line):
filter_ops = [ op_name(op) for op in op_line.split() ]
return ' '.join(filter_ops)
def gen_doc(opfile, docfile):
op = pd.read_csv(opfile, compression='xz', index_col=0)
op.dropna(inplace=True)
op['Opcodes'] = op['Opcodes'].apply(filter_op)
def get_model(opfile, binfile, size=5):
docfile = 'op-doc.tmp.txt'
gen_doc(opfile, docfile)
logging.info('Training opcode word2vec...in=%s, out=%s, word-embed-size=%d' % (docfile, binfile, size))
word2vec.word2vec(docfile, binfile, size=size, verbose=True)
return word2vec.load(binfile)
```
For the Code snippet:
```
op_vecs = [ opline_to_vec(row['Opcodes'], w2v) for idx, row in data.iterrows() ]
```
invokes function
```
def opline_to_vec(line, w2v):
print('inside oplinetovec func')
ops = line.split()
print('ops and line.split done')
vec = np.zeros((len(ops), w2v.vectors.shape[1]))
print('vec computed')
for i, op in enumerate(ops):
print('each vec i values')
vec[i] = w2v.get_vector(op_name(op))***
print(vec[i])
print ('returning from opline_to_vec')
return vec
the output of op-doc-temp.txt-->
CALLDATASIZE SUB DUP ADD SWAP DUP DUP CALLDATALOAD PUSH AND SWAP PUSH ADD SWAP SWAP SWAP SWAP POP POP POP PUSH JUMP JUMPDEST PUSH MLOAD DUP DUP DUP MSTORE PUSH ADD SWAP POP POP PUSH MLOAD DUP SWAP SUB SWAP RETURN JUMPDEST PUSH PUSH DUP CALLDATASIZE SUB DUP ADD SWAP DUP DUP CALLDATALOAD PUSH AND SWAP PUSH ADD SWAP SWAP SWAP SWAP DUP CALLDATALOAD SWAP PUSH ADD SWAP DUP ADD DUP CALLDATALOAD SWAP PUSH ADD SWAP SWAP SWAP SWAP SWAP SWAP SWAP SWAP SWAP POP POP POP PUSH JUMP JUMPDEST STOP JUMPDEST CALLVALUE DUP ISZERO PUSH JUMPI PUSH DUP REVERT JUMPDEST POP PUSH PUSH DUP
I have highlighted the code snippet(vec[i] = w2v.get_vector(op_name(op))) which produces the error:
/usr/local/lib/python3.7/dist-packages/word2vec/wordvectors.py in ix(self, word)
36 Returns the index on `self.vocab` and `self.vectors` for `word`
37 """
---> 38 return self.vocab_hash[word]
39
40 def word(self, ix):
KeyError: 'CALLDATASIZE'
enter image description here
It would be really great if you could please help
It looks like you're asking a word-vectors model for the vector of a word, 'CALLDATASIZE', that it does not know.
Where did the set of word-vectors come from? (Did you train them yourself, or import them from elsewhere? How did you load them?)
Would you expect it to have a vector for that weird opcode-word? If so, skip the other wraparound steps and just check for that word, and go back to the prior steps that you thought should have created that word-vector.
If it's reasonable the set doesn't have that word, and you can't fix that gap, change your code to handle that case - perhaps by ignoring the word.

When using the frame skipping wrapper for OpenAI Gym, what is the purpose of the np.max line?

I'm implementing the following wrapper used commonly in OpenAI's Gym for Frame Skipping. It can be found in dqn/atari_wrappers.py
I'm very confused about the following line:
max_frame = np.max(np.stack(self._obs_buffer), axis=0)
I have added comments throughout the code for the parts I understand and to aid anyone who may be able to help.
np.stack(self._obs_buffer) stacks the two states in _obs_buffer.
np.max returns the maximum along axis 0.
But what I don't understand is why we're doing this or what it's really doing.
class MaxAndSkipEnv(gym.Wrapper):
"""Return only every 4th frame"""
def __init__(self, env=None, skip=4):
super(MaxAndSkipEnv, self).__init__(env)
# Initialise a double ended queue that can store a maximum of two states
self._obs_buffer = deque(maxlen=2)
# _skip = 4
self._skip = skip
def _step(self, action):
total_reward = 0.0
done = None
for _ in range(self._skip):
# Take a step
obs, reward, done, info = self.env.step(action)
# Append the new state to the double ended queue buffer
self._obs_buffer.append(obs)
# Update the total reward by summing the (reward obtained from the step taken) + (the current
# total reward)
total_reward += reward
# If the game ends, break the for loop
if done:
break
max_frame = np.max(np.stack(self._obs_buffer), axis=0)
return max_frame, total_reward, done, info
At the end of the for loop the self._obs_buffer holds the last two frames.
Those two frames are then max-pooled over, resulting in an observation, that contains some temporal information.

How to automatically crop an .OBJ 3D model to a bounding box?

In the now obsoleted Autodesk ReCap API it was possible to specify a "bounding box" around the scene to be generated from images.
In the resulting models, any vertices outside the bounding box were discarded, and any volumes that extended beyond the bounding box were truncated to have faces at the box boundaries.
I am now using Autodesk's Forge Reality Capture API which replaced ReCap. Apparently, This new API does not allow the user to specify a bounding box.
So I am now searching for a program that takes an .OBJ file and a specified bounding box as input, and outputs a file of just the vertices and faces within this bounding box.
Given that there is no way to specify the bounding box in Reality Capture API, I created this python program. It is crude, in that it only discards faces that have vertices that are outside the bounding box. And it actually does discards nondestructively, only by commenting them out in the output OBJ file. This allows you to uncomment them and then use a different bounding box.
This may not be what you need if you truly want to remove all relevant v, vn, vt, vp and f lines that are outside the bounding box, because the OBJ file size remains mostly unchanged. But for my particular needs, keeping all the records and just using comments was preferable.
# obj3Dcrop.py
# (c) Scott L. McGregor, Dec 2019
# License: free for all non commercial uses. Contact author for any other uses.
# Changes and Enhancements must be shared with author, and be subject to same use terms
# TL;DR: This program uses a bounding box, and "crops" faces and vertices from a
# Wavefront .OBJ format file, created by Autodesk Forge Reality Capture API
# if one of the vertices in a face is not within the bounds of the box.
#
# METHOD
# 1) All lines other than "v" vertex definitions and "f" faces definitions
# are copied UNCHANGED from the input .OBJ file to an output .OBJ file.
# 2) All "v" vertex definition lines have their (x, y, z) positions tested to see if:
# minX < x < maxX and minY < y < maxY and minZ < z < maxZ ?
# If TRUE, we want to keep this vertex in the new OBJ, so we
# store its IMPLICIT ORDINAL position in the file in a dictionary called v_keepers.
# If FALSE, we will use its absence from the v_keepers file as a way to identify
# faces that contain it and drop them. All "v" lines are also copied unchanged to the
# output file.
# 3) All "f" lines (face definitions) are inspected to verify that all 3 vertices in the face
# are in the v_keepers list. If they are, the f line is output unchanged.
# 4) Any "f" line that refers to a vertex that was cropped, is prefixed by "# CROPPED: "
# in the output file. Lines beginning # are treated as comments, and ignored in future
# processing.
# KNOWN LIMITATIONS: This program generates models in which the outside of bound faces
# have been removed. The vertices that were found outside the bounding box, are still in the
# OBJ file, but they are now disconnected and therefore ignored in later processing.
# The "f" lines for faces with vertices outside the bounding box are also still in the
# output file, but now commented out, so they don't process. Because this is non-destructive.
# we can easily change our bounding box later, uncomment cropped lines and reprocess.
#
# This might be an incomplete solution for some potential users. For such users
# a more complete program would delete unneeded v, vn, vt and vp lines when the v vertex
# that they refer to is dropped. But note that this requires renumbering all references to these
# vertice definitions in the "f" face definition lines. Such a more complete solution would also
# DISCARD all 'f' lines with any vertices that are out of bounds, instead of making them copies.
# Such a rewritten .OBJ file would be var more compact, but changing the bounding box would require
# saving the pre-cropped original.
# QUIRK: The OBJ file format defines v, vn, vt, vp and f elements by their
# IMPLICIT ordinal occurrence in the file, with each element type maintaining
# its OWN separate sequence. It then references those definitions EXPLICITLY in
# f face definitions. So deleting (or commenting out) element references requires
# appropriate rewriting of all the"f"" lines tracking all the new implicit positions.
# Such rewriting is not particularly hard to do, but it is one more place to make
# a mistake, and could make the algorithm more complicated to understand.
# This program doesn't bother, because all further processing of the output
# OBJ file ignores unreferenced v, vn, vt and vp elements.
#
# Saving all lines rather than deleting them to save space is a tradeoff involving considerations of
# Undo capability, compute cycles, compute space (unreferenced lines) and maintenance complexity choice.
# It is left to the motivated programmer to add this complexity if needed.
import sys
#bounding_box = sys.argv[1] # should be in the only string passsed (maxX, maxY, maxZ, minX, minY, minZ)
bounding_box = [10, 10, 10, -10, -10, 1]
maxX = bounding_box[0]
maxY = bounding_box[1]
maxZ = bounding_box[2]
minX = bounding_box[3]
minY = bounding_box[4]
minZ = bounding_box[5]
v_keepers = dict() # keeps track of which vertices are within the bounding box
kept_vertices = 0
discarded_vertices = 0
kept_faces = 0
discarded_faces = 0
discarded_lines = 0
kept_lines = 0
obj_file = open('sample.obj','r')
new_obj_file = open('cropped.obj','w')
# the number of the next "v" vertex lines to process.
original_v_number = 1 # the number of the next "v" vertex lines to process.
new_v_number = 1 # the new ordinal position of this vertex if out of bounds vertices were discarded.
for line in obj_file:
line_elements = line.split()
# Python doesn't have a SWITCH statement, but we only have three cases, so we'll just use cascading if stmts
if line_elements[0] != "f": # if it isn't an "f" type line (face definition)
if line_elements[0] != "v": # and it isn't an "v" type line either (vertex definition)
# ************************ PROCESS ALL NON V AND NON F LINE TYPES ******************
# then we just copy it unchanged from the input OBJ to the output OBJ
new_obj_file.write(line)
kept_lines = kept_lines + 1
else: # then line_elements[0] == "v":
# ************************ PROCESS VERTICES ****************************************
# a "v" line looks like this:
# f x y z ...
x = float(line_elements[1])
y = float(line_elements[2])
z = float(line_elements[3])
if minX < x < maxX and minY < y < maxY and minZ < z < maxZ:
# if vertex is within the bounding box, we include it in the new OBJ file
new_obj_file.write(line)
v_keepers[str(original_v_number)] = str(new_v_number)
new_v_number = new_v_number + 1
kept_vertices = kept_vertices +1
kept_lines = kept_lines + 1
else: # if vertex is NOT in the bounding box
new_obj_file.write(line)
discarded_vertices = discarded_vertices +1
discarded_lines = discarded_lines + 1
original_v_number = original_v_number + 1
else: # line_elements[0] == "f":
# ************************ PROCESS FACES ****************************************
# a "f" line looks like this:
# f v1/vt1/vn1 v2/vt2/vn2 v3/vt3/vn3 ...
# We need to delete any face lines where ANY of the 3 vertices v1, v2 or v3 are NOT in v_keepers.
v = ["", "", ""]
# Note that v1, v2 and v3 are the first "/" separated elements within each line element.
for i in range(0,3):
v[i] = line_elements[i+1].split('/')[0]
# now we can check if EACH of these 3 vertices are in v_keepers.
# for each f line, we need to determine if all 3 vertices are in the v_keepers list
if v[0] in v_keepers and v[1] in v_keepers and v[2] in v_keepers:
new_obj_file.write(line)
kept_lines = kept_lines + 1
kept_faces = kept_faces +1
else: # at least one of the vertices in this face has been deleted, so we need to delete the face too.
discarded_lines = discarded_lines + 1
discarded_faces = discarded_faces +1
new_obj_file.write("# CROPPED "+line)
# end of line processing loop
obj_file.close()
new_obj_file.close()
print ("kept vertices: ", kept_vertices ,"discarded vertices: ", discarded_vertices)
print ("kept faces: ", kept_faces, "discarded faces: ", discarded_faces)
print ("kept lines: ", kept_lines, "discarded lines: ", discarded_lines)
Unfortunately, (at least for now) there is no way to specify the bounding box in Reality Capture API.

Code Golf: Lasers

Locked. This question and its answers are locked because the question is off-topic but has historical significance. It is not currently accepting new answers or interactions.
The challenge
The shortest code by character count to input a 2D representation of a board, and output 'true' or 'false' according to the input.
The board is made out of 4 types of tiles:
# - A solid wall
x - The target the laser has to hit
/ or \ - Mirrors pointing to a direction (depends on laser direction)
v, ^, > or < - The laser pointing to a direction (down, up, right and left respectively)
There is only one laser and only one target. Walls must form a solid rectangle of any size, where the laser and target are placed inside. Walls inside the 'room' are possible.
Laser ray shots and travels from its origin to the direction it's pointing. If a laser ray hits the wall, it stops. If a laser ray hits a mirror, it bounces 90 degrees to the direction the mirror points to. Mirrors are two sided, meaning both sides are 'reflective' and may bounce a ray in two ways. If a laser ray hits the laser (^v><) itself, it is treated as a wall (laser beam destroys the beamer and so it'll never hit the target).
Test cases
Input:
##########
# / \ #
# #
# \ x#
# > / #
##########
Output:
true
Input:
##########
# v x #
# / #
# /#
# \ #
##########
Output:
false
Input:
#############
# # #
# > # #
# # #
# # x #
# # #
#############
Output:
false
Input:
##########
#/\/\/\ #
#\\//\\\ #
#//\/\/\\#
#\/\/\/x^#
##########
Output:
true
Code count includes input/output (i.e full program).
Perl, 166 160 characters
Perl, 251 248 246 222 214 208 203 201 193 190 180 176 173 170 166 --> 160 chars.
Solution had 166 strokes when this contest ended, but A. Rex has found a couple ways to shave off 6 more characters:
s!.!$t{$s++}=$&!ge,$s=$r+=99for<>;%d='>.^1<2v3'=~/./g;($r)=grep$d|=$d{$t{$_}},%t;
{$_=$t{$r+=(1,-99,-1,99)[$d^=3*/\\/+m</>]};/[\/\\ ]/&&redo}die/x/?true:false,$/
The first line loads the input into %t, a table of the board where $t{99*i+j} holds the character at row i,column j. Then,
%d=split//,'>.^1<2v3' ; ($r)=grep{$d|=$d{$t{$_}}}%t
it searches the elements of %t for a character that matches > ^ < or v, and simultaneously sets $d to a value between 0 and 3 that indicates the initial direction of the laser beam.
At the beginning of each iteration in the main loop, we update $d if the beam is currently on a mirror. XOR'ing by 3 gives the correct behavior for a \ mirror and XOR'ing by 1 gives the correct behavior for a / mirror.
$d^=3*/\\/+m</>
Next, the current position $r is updated accoring to the current direction.
$r+=(1,-99,-1,99)[$d] ; $_ = $t{$r}
We assign the character at the current position to $_ to make convenient use of the match operators.
/[\/\\ ]/ && redo
Continue if we are on a blank space or a mirror character. Otherwise we terminate true if we are on the target ($_ =~ /x/) and false otherwise.
Limitation: may not work on problems with more than 99 columns. This limitation could be removed at the expense of 3 more characters,
Perl, 177 Characters
The first linebreak can be removed; the other two are mandatory.
$/=%d=split//,' >/^\v';$_=<>;$s='#';{
y/v<^/>v</?do{my$o;$o.="
"while s/.$/$o.=$&,""/meg;y'/\\'\/'for$o,$s;$_=$o}:/>x/?die"true
":/>#/?die"false
":s/>(.)/$s$d{$1}/?$s=$1:1;redo}
Explanation:
$/ = %d = (' ' => '>', '/' => '^', '\\' => 'v');
If a right-moving beam runs into an {empty space, up-angled mirror, down-angled mirror} it becomes a {right-moving beam, up-moving beam, down-moving beam}. Initialize $/ along the way -- fortunately "6" is not a valid input char.
$_ = <>;
Read the board into $_.
$s="#";
$s is the symbol of whatever the beam is sitting on top of now. Since the laser emitter is to be treated like a wall, set this to be a wall to begin with.
if (tr/v<^/>v</) {
my $o;
$o .= "\n" while s/.$/$o .= $&, ""/meg;
tr,/\\,\\/, for $o, $s;
$_ = $o;
}
If the laser beam is pointing any way except right, rotate its symbol, and then rotate the whole board in place (also rotating the symbols for the mirrors). It's a 90 degree left rotation, accomplished effectively by reversing the rows while transposing rows and columns, in a slightly fiendish s///e with side effects. In the golfed code, the tr is written in the form y''' which allows me to skip backslashing one backslash.
die "true\n" if />x/; die "false\n" if />#/;
Terminate with the right message if we hit the target or a wall.
$s = $1 if s/>(.)/$s$d{$1}/;
If there's an empty space in front of the laser, move forward. If there's a mirror in front of the laser, move forward and rotate the beam. In either case, put the "saved symbol" back into the old beam location, and put the thing we just overwrote into the saved symbol.
redo;
Repeat until termination. {...;redo} is two characters less than for(;;){...} and three less than while(1){...}.
C89 (209 characters)
#define M(a,b)*p==*#a?m=b,*p=1,q=p:
*q,G[999],*p=G;w;main(m){for(;(*++p=getchar())>0;)M(<,-1)M
(>,1)M(^,-w)M(v,w)!w&*p<11?w=p-G:0;for(;q+=m,m=*q&4?(*q&1?
-1:1)*(m/w?m/w:m*w):*q&9?!puts(*q&1?"false":"true"):m;);}
Explanation
This monstrosity will probably be difficult to follow if you don't understand C. Just a forewarning.
#define M(a,b)*p==*#a?m=b,*p=1,q=p:
This little macro checks if the current character (*p) is equal to whatever a is in character form (*#a). If they are equal, set the movement vector to b (m=b), mark this character as a wall (*p=1), and set the starting point to the current location (q=p). This macro includes the "else" portion.
*q,G[999],*p=G;
w;
Declare some variables.
* q is the light's current location.
* G is the game board as a 1D array.
* p is the current read location when populating G.
* w is the board's width.
main(m){
Obvious main. m is a variable storing the movement vector. (It's a parameter to main as an optimization.)
for(;(*++p=getchar())>0;)
Loop through all characters, populating G using p. Skip G[0] as an optimization (no need to waste a character writing p again in the third part of the for).
M(<,-1)
M(>,1)
M(^,-w)
M(v,w)
Use the aforementioned macro to define the lazer, if possible. -1 and 1 correspond to left and right, respectively, and -w and w up and down.
!w&*p<11
?w=p-G
:0;
If the current character is an end-of-line marker (ASCII 10), set the width if it hasn't already been set. The skipped G[0] allows us to write w=p-G instead of w=p-G+1. Also, this finishes off the ?: chain from the M's.
for(;
q+=m,
Move the light by the movement vector.
m=
*q&4
?(*q&1?-1:1)*(
m/w?m/w:m*w
)
Reflect the movement vector.
:*q&9
?!puts(*q&1?"false":"true")
:m
;
If this is a wall or x, quit with the appropriate message (m=0 terminates the loop). Otherwise, do nothing (noop; m=m)
);
}
I would bet people have been waiting for this one for a LOOOOONG time. (What do you mean, the challenge is over and nobody cares any more?)
Behold... I here present a solution in
Befunge-93!
It weighs in at a whopping 973 charaters (or 688 if you are charitable enough to ignore whitespace, which is only used for formatting and does nothing in actual code).
Caveat: I wrote my own Befunge-93 interpreter in Perl a short while ago, and unfortunately this is all I've really had time with which to test it. I'm reasonably confident in its correctness in general, but it might have an odd limitation with regard to EOF: Since Perl's <> operator returns undef at the end of file, this is processed as a 0 in the numeric context. For C-based implementations where EOF has a different value (-1 say), this code might not work.
003pv >~v> #v_"a"43g-!#v_23g03p33v>v
>39#<*v :: >:52*-!v >"rorrE",vg2*
######1 >^vp31+1g31$_03g13gp vv,,<15,
a#3 >0v vp30+1g30<>,,#3^#
######p $ 0vg34"a"< > >vp
^<v> > ^ p3<>-#v_:05g-!|>:15g-!| $
> v^ < < < >^v-g52:< $
v _ >52*"eslaf",,vv|-g53:_ v
: ^-"#">#:< ##,,,,<<>:43p0 v0 p34<
>">"-!vgv< ^0p33g31p32-1g3<
^ <#g1|-g34_v#-g34_v#-g34"><v^"<<<<
v!<^<33>13g1v>03g1-v>03g1+03p$v $$
>^ _#-v 1>g1-1v>+13pv >03p v pp
^_:"^"^#|^g30 <3# $< $<>^33
^!-"<":<>"v"v^># p#$<> $^44
^ >#^#_ :" "-#v_ ^ > ^gg
v g34$< ^!<v"/":< >$3p$^>05g43p$ ^55
>,# |!-"\" :_$43g:">"-!|> ^$32
*v"x":< >-^ ^4g52<>:"^" -#v_^
5>-!#v_"ror"vv$p34g51:<>#| !-"<":<#|
^2,,, ,,"er"<>v #^^#<>05g43p$$^>^
>52*"eurt",,,,,#>15g4 3p$$$$ ^#
>:"v"\:"<"\: "^" -!#^_-!#^_-! ^
> ^
Explanation
If you're not familiar with the Befunge syntax and operation, check here.
Befunge is a stack-based language, but there are commands that allow one to write characters to the Befunge code. I take advantage of that in two places. First, I copy the entire input onto the Befunge board, but located a couple of lines below the actual written code. (Of course, this is never actually visible when the code runs.)
The other place is near the the upper-left:
######
a#
######
In this case, the area I've highlighted above is where I store a couple of coordinates. The first column in the middle row there is where I store the x-coordinate for the current "cursor position"; the second column is where I store the y-coordinate; the next two columns are for storing the x- and y-coordinate of the laser beam source when that is found; and the final column (with the 'a' character in it) is eventually overwritten to contain the current beam direction, which obviously changes as the beam's path is traced.
The program starts by placing (0,27) as the initial cursor position. Then input is read one character at a time and placed in the cursor position; newlines merely cause the y-coordinate to increase and the x-coordinate to go back to 0, just like a real carriage return. Eventually undef is read by the interpreter and that 0 character value is used to signal the end of input and move on to the laser iteration steps. When the laser character [<>^v] is read, that is also copied to the memory repository (over the 'a' character) and its coordinates are copied to the columns just to the left.
The end result of all of this is that the entire file is basically copied into the Befunge code, a little ways below the actual code traversed.
Afterwards, the beam location is copied back into the cursor locations, and the following iteration is performed:
Check for the current beam direction and increment or decrement the cursor coordinates appropriately. (I do this first to avoid having to deal with the corner case of the laser beam right on the first move.)
Read the character at that location.
If the character is "#", put newline and "false" on the stack, print, and end.
Compare it to all of the beam characters [<>^v]; if there's a match, also print "false\n" and end.
If the character is a space, empty the stack and continue.
If the character is a forward slash, get the beam direction onto the stack and compare it to each of the direction characters in turn. When one is found, the new direction is stored at that same spot in the code and the loop repeats.
If the character is a backslash, do basically the same thing as the above (except with the proper mapping for backslash).
If the character is 'x', we've hit the target. Print "true\n" and exit.
If the character is none of these, print "error\n" and exit.
If there's enough demand for it, I'll try to point out exactly where in the code all this is accomplished.
F#, 36 lines, very readable
Ok, just to get an answer out there:
let ReadInput() =
let mutable line = System.Console.ReadLine()
let X = line.Length
let mutable lines = []
while line <> null do
lines <- Seq.to_list line :: lines
line <- System.Console.ReadLine()
lines <- List.rev lines
X, lines.Length, lines
let X,Y,a = ReadInput()
let mutable p = 0,0,'v'
for y in 0..Y-1 do
for x in 0..X-1 do
printf "%c" a.[y].[x]
match a.[y].[x] with
|'v'|'^'|'<'|'>' -> p <- x,y,a.[y].[x]
|_ -> ()
printfn ""
let NEXT = dict [ '>', (1,0,'^','v')
'v', (0,1,'<','>')
'<', (-1,0,'v','^')
'^', (0,-1,'>','<') ]
let next(x,y,d) =
let dx, dy, s, b = NEXT.[d]
x+dx,y+dy,(match a.[y+dy].[x+dx] with
| '/' -> s
| '\\'-> b
| '#'|'v'|'^'|'>'|'<' -> printfn "false"; exit 0
| 'x' -> printfn "true"; exit 0
| ' ' -> d)
while true do
p <- next p
Samples:
##########
# / \ #
# #
# \ x#
# > / #
##########
true
##########
# v x #
# / #
# /#
# \ #
##########
false
#############
# # #
# > # #
# # #
# # x #
# # #
#############
false
##########
#/\/\/\ #
#\\//\\\ #
#//\/\/\\#
#\/\/\/x^#
##########
true
##########
# / \ #
# #
#/ \ x#
#\> / #
##########
false
##########
# / \#
# / \ #
#/ \ x#
#\^/\ / #
##########
false
Golfscript - 83 chars (mashup of mine and strager's)
The newline is just here for wrapping
:|'v^><'.{|?}%{)}?:$#=?{.[10|?).~)1-1]=$+
:$|=' \/x'?\[.\2^.1^'true''false']=.4/!}do
Golfscript - 107 chars
The newline is just there for clarity
10\:#?):&4:$;{0'>^<v'$(:$=#?:*>}do;
{[1 0&--1&]$=*+:*;[{$}{3$^}{1$^}{"true "}{"false"}]#*=' \/x'?=~5\:$>}do$
How it works.
First line works out the initial location and direction.
Second line steps through turning whenever the laser hits a mirror.
353 chars in Ruby:
314 277 chars now!
OK, 256 chars in Ruby and now I'm done. Nice round number to stop at. :)
247 chars. I can't stop.
223 203 201 chars in Ruby
d=x=y=-1;b=readlines.each{|l|d<0&&(d="^>v<".index l[x]if x=l.index(/[>^v<]/)
y+=1)};loop{c=b[y+=[-1,0,1,0][d]][x+=[0,1,0,-1][d]]
c==47?d=[1,0,3,2][d]:c==92?d=3-d:c==35?(p !1;exit):c<?x?0:(p !!1;exit)}
With whitespace:
d = x = y = -1
b = readlines.each { |l|
d < 0 && (d = "^>v<".index l[x] if x = l.index(/[>^v<]/); y += 1)
}
loop {
c = b[y += [-1, 0, 1, 0][d]][x += [0, 1, 0, -1][d]]
c == 47 ? d = [1, 0, 3, 2][d] :
c == 92 ? d = 3 - d :
c == 35 ? (p !1; exit) :
c < ?x ? 0 : (p !!1; exit)
}
Slightly refactored:
board = readlines
direction = x = y = -1
board.each do |line|
if direction < 0
x = line.index(/[>^v<]/)
if x
direction = "^>v<".index line[x]
end
y += 1
end
end
loop do
x += [0, 1, 0, -1][direction]
y += [-1, 0, 1, 0][direction]
ch = board[y][x].chr
case ch
when "/"
direction = [1, 0, 3, 2][direction]
when "\\"
direction = 3 - direction
when "x"
puts "true"
exit
when "#"
puts "false"
exit
end
end
Python
294 277 253 240 232 characters including newlines:
(the first character in lines 4 and 5 is a tab, not spaces)
l='>v<^';x={'/':'^<v>','\\':'v>^<',' ':l};b=[1];r=p=0
while b[-1]:
b+=[raw_input()];r+=1
for g in l:
c=b[r].find(g)
if-1<c:p=c+1j*r;d=g
while' '<d:z=l.find(d);p+=1j**z;c=b[int(p.imag)][int(p.real)];d=x.get(c,' '*4)[z]
print'#'<c
I had forgotten Python even had optional semicolons.
How it works
The key idea behind this code is using complex numbers to represent positions and directions. The rows are the imaginary axis, increasing downward. The columns are the real axis, increasing to the right.
l='>v<^'; a list of the laser symbols. The order is chosen so that the index of a laser direction character corresponds with a power of sqrt(-1)
x={'/':'^<v>','\\':'v>^<',' ':l}; a transformation table determining how the direction changes when the beam leaves different tiles. The tile is the key, and new directions are the values.
b=[1]; holds the board. The first element is 1 (evaluates as true) so that the while loop will run at least once.
r=p=0 r is the current row number of the input, p is the current position of the laser beam.
while b[-1]: stop loading board data when raw_input returns an empty string
b+=[raw_input()];r+=1 append the next line of input to the board and increment the row counter
for g in l: guess each laser direction in turn
c=b[r].find(g) set the column to the location of the laser or -1 if it's not in the line (or is pointing in a different direction)
if-1<c:p=c+1j*r;d=g if we found a laser, then set the current position p and direction d. d is one of the chars in l
After loading the board into b, the current position p and direction d have been set to those of the laser source.
while' '<d: space has a lower ASCII value than any of the direction symbols, so we use it as a stop flag.
z=l.find(d); index of the current direction char in the l string. z gets used later to both determine the new beam direction using the x table, and to increment the position.
p+=1j**z; increment the position using a power of i. For example, l.find('<')==2 -> i^2 = -1, which would move to the left one column.
c=b[int(p.imag)][int(p.real)]; read the char at the current position
d=x.get(c,' '*4)[z] look up the new direction for the beam in the transformation table. If the current char doesn't exist in the table, then set d to space.
print'#'<c print false if we stopped on anything other than the target.
This is was a direct port of Brian's solution to C#3, minus the console interactions.
This isn't an entry in the challenge since it isn't a complete program, I was just wondering how some of the F# constructs he used could be represented in C#.
bool Run(string input) {
var a = input.Split(new[] {Environment.NewLine}, StringSplitOptions.None);
var p = a.SelectMany((line, y) => line.Select((d, x) => new {x, y, d}))
.First(x => new[] {'v', '^', '<', '>'}.Contains(x.d));
var NEXT = new[] {
new {d = '>', dx = 1, dy = 0, s = '^', b = 'v'},
new {d = 'v', dx = 0, dy = 1, s = '<', b = '>'},
new {d = '<', dx = -1, dy = 0, s = 'v', b = '^'},
new {d = '^', dx = 0, dy = -1, s = '>', b = '<'}
}.ToDictionary(x => x.d);
while (true) {
var n = NEXT[p.d];
int x = p.x + n.dx,
y = p.y + n.dy;
var d = a[y][x];
switch (d) {
case '/': d = n.s; break;
case '\\': d = n.b; break;
case ' ': d = p.d; break;
default: return d == 'x';
}
p = new {x, y, d};
}
}
Edit: After some experimentation, the following rather verbose search code:
int X = a[0].Length, Y = a.Length;
var p = new {x = 0, y = 0, d = 'v'};
for (var y = 0; y < Y; y++) {
for (var x = 0; x < X; x++) {
var d = a[y][x];
switch (d) {
case 'v': case '^': case '<': case '>':
p = new {x, y, d}; break;
}
}
}
has been replaced with some much more compact LINQ to Objects code:
var p = a.SelectMany((line, y) => line.Select((d, x) => new {x, y, d}))
.First(x => new[] {'v', '^', '<', '>'}.Contains(x.d));
F#, 255 chars (and still rather readable!):
Ok, after a night's rest, I improved this a lot:
let a=System.Console.In.ReadToEnd()
let w,c=a.IndexOf"\n"+1,a.IndexOfAny[|'^';'<';'>';'v'|]
let rec n(c,d)=
let e,s=[|-w,2;-1,3;1,0;w,1|].[d]
n(c+e,match a.[c+e]with|'/'->s|'\\'->3-s|' '->d|c->printfn"%A"(c='x');exit 0)
n(c,"^<>v".IndexOf a.[c])
Let's talk through it line by line.
First, slurp all the input into a big one-dimensional array (2D arrays can be bad for code golf; just use a 1D array and add/subtract the width of one line to the index to move up/down a line).
Next we compute 'w', the width of an input line, and 'c', the starting position, by indexing into our array.
Now let's define the 'next' function 'n', which takes a current position 'c' and a direction 'd' which is 0,1,2,3 for up,left,right,down.
The index-epsilon 'e' and the what-new-direction-if-we-hit-a-slash 's' are computed by a table. For example, if the current direction 'd' is 0 (up), then the first element of the table says "-w,2" which means we decrement the index by w, and if we hit a slash the new direction is 2 (right).
Now we recurse into the next function 'n' with (1) the next index ("c+e" - current plus epsilon), and (2) the new direction, which we compute by looking ahead to see what's in the array in that next cell. If the lookahead char is a slash, the new direction is 's'. If it's a backslash, the new direction is 3-s (our choice of encoding 0123 makes this work). If it's a space, we just keep going in the same direction 'd'. And if it's any other character 'c', then the game ends, printing 'true' if the char was 'x' and false otherwise.
To kick things off, we call the recursive function 'n' with the initial position 'c' and the starting direction (which does the initial encoding of direction into 0123).
I think I can probably still shave a few more characters off it, but I am pretty pleased with it like this (and 255 is a nice number).
Weighing in at 18203 characters is a Python solution that can:
cope with mirrors outside of the
'room'
calculate the trajectory when there is no 'room' on the basis of 2D limitations (the spec says lots about what has to be in the 'room' but not if the room has to exist)
report back on errors
It still needs tidied up somewhat and I do not know if 2D physics dictate that the beam cannot cross itself...
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
The shortest code by character count to input a 2D representation of a board,
and output 'true' or 'false' according to the input.
The board is made out of 4 types of tiles:
# - A solid wall
x - The target the laser has to hit
/ or \ - Mirrors pointing to a direction (depends on laser direction)
v, ^, > or < - The laser pointing to a direction (down, up, right and left
respectively)
There is only one laser and only one target. Walls must form a solid rectangle
of any size, where the laser and target are placed inside. Walls inside the
'room' are possible.
Laser ray shots and travels from it's origin to the direction it's pointing. If
a laser ray hits the wall, it stops. If a laser ray hits a mirror, it is bounces
90 degrees to the direction the mirror points to. Mirrors are two sided, meaning
both sides are 'reflective' and may bounce a ray in two ways. If a laser ray
hits the laser (^v><) itself, it is treated as a wall (laser beam destroys the
beamer and so it'll never hit the target).
"""
SOLID_WALL, TARGET, MIRROR_NE_SW, MIRROR_NW_SE, LASER_DOWN, LASER_UP, \
LASER_RIGHT, LASER_LEFT = range(8)
MIRRORS = (MIRROR_NE_SW, MIRROR_NW_SE)
LASERS = (LASER_DOWN, LASER_UP, LASER_RIGHT, LASER_LEFT)
DOWN, UP, RIGHT, LEFT = range(4)
LASER_DIRECTIONS = {
LASER_DOWN : DOWN,
LASER_UP : UP,
LASER_RIGHT: RIGHT,
LASER_LEFT : LEFT
}
ROW, COLUMN = range(2)
RELATIVE_POSITIONS = {
DOWN : (ROW, 1),
UP : (ROW, -1),
RIGHT: (COLUMN, 1),
LEFT : (COLUMN, -1)
}
TILES = {"#" : SOLID_WALL,
"x" : TARGET,
"/" : MIRROR_NE_SW,
"\\": MIRROR_NW_SE,
"v" : LASER_DOWN,
"^" : LASER_UP,
">" : LASER_RIGHT,
"<" : LASER_LEFT}
REFLECTIONS = {MIRROR_NE_SW: {DOWN : LEFT,
UP : RIGHT,
RIGHT: UP,
LEFT : DOWN},
MIRROR_NW_SE: {DOWN : RIGHT,
UP : LEFT,
RIGHT: DOWN,
LEFT : UP}}
def does_laser_hit_target(tiles):
"""
Follows a lasers trajectory around a grid of tiles determining if it
will reach the target.
Keyword arguments:
tiles --- row/column based version of a board containing symbolic
versions of the tiles (walls, laser, target, etc)
"""
#Obtain the position of the laser
laser_pos = get_laser_pos(tiles)
#Retrieve the laser's tile
laser = get_tile(tiles, laser_pos)
#Create an editable starting point for the beam
beam_pos = list(laser_pos)
#Create an editable direction for the beam
beam_dir = LASER_DIRECTIONS[laser]
#Cache the number of rows
number_of_rows = len(tiles)
#Keep on looping until an ultimate conclusion
while True:
#Discover the axis and offset the beam is travelling to
axis, offset = RELATIVE_POSITIONS[beam_dir]
#Modify the beam's position
beam_pos[axis] += offset
#Allow for a wrap around in this 2D scenario
try:
#Get the beam's new tile
tile = get_tile(tiles, beam_pos)
#Perform wrapping
except IndexError:
#Obtain the row position
row_pos = beam_pos[ROW]
#Handle vertical wrapping
if axis == ROW:
#Handle going off the top
if row_pos == -1:
#Move beam to the bottom
beam_pos[ROW] = number_of_rows - 1
#Handle going off the bottom
elif row_pos == number_of_rows:
#Move beam to the top
beam_pos[ROW] = 0
#Handle horizontal wrapping
elif axis == COLUMN:
#Obtain the row
row = tiles[row_pos]
#Calculate the number of columns
number_of_cols = len(row)
#Obtain the column position
col_pos = beam_pos[COLUMN]
#Handle going off the left hand side
if col_pos == -1:
#Move beam to the right hand side
beam_pos[COLUMN] = number_of_cols - 1
#Handle going off the right hand side
elif col_pos == number_of_cols:
#Move beam to the left hand side
beam_pos[COLUMN] = 0
#Get the beam's new tile
tile = get_tile(tiles, beam_pos)
#Handle hitting a wall or the laser
if tile in LASERS \
or tile == SOLID_WALL:
return False
#Handle hitting the target
if tile == TARGET:
return True
#Handle hitting a mirror
if tile in MIRRORS:
beam_dir = reflect(tile, beam_dir)
def get_laser_pos(tiles):
"""
Returns the current laser position or an exception.
Keyword arguments:
tiles --- row/column based version of a board containing symbolic
versions of the tiles (walls, laser, target, etc)
"""
#Calculate the number of rows
number_of_rows = len(tiles)
#Loop through each row by index
for row_pos in range(number_of_rows):
#Obtain the current row
row = tiles[row_pos]
#Calculate the number of columns
number_of_cols = len(row)
#Loop through each column by index
for col_pos in range(number_of_cols):
#Obtain the current column
tile = row[col_pos]
#Handle finding a laser
if tile in LASERS:
#Return the laser's position
return row_pos, col_pos
def get_tile(tiles, pos):
"""
Retrieves a tile at the position specified.
Keyword arguments:
pos --- a row/column position of the tile
tiles --- row/column based version of a board containing symbolic
versions of the tiles (walls, laser, target, etc)
"""
#Obtain the row position
row_pos = pos[ROW]
#Obtain the column position
col_pos = pos[COLUMN]
#Obtain the row
row = tiles[row_pos]
#Obtain the tile
tile = row[col_pos]
#Return the tile
return tile
def get_wall_pos(tiles, reverse=False):
"""
Keyword arguments:
tiles --- row/column based version of a board containing symbolic
versions of the tiles (walls, laser, target, etc)
reverse --- whether to search in reverse order or not (defaults to no)
"""
number_of_rows = len(tiles)
row_iter = range(number_of_rows)
if reverse:
row_iter = reversed(row_iter)
for row_pos in row_iter:
row = tiles[row_pos]
number_of_cols = len(row)
col_iter = range(number_of_cols)
if reverse:
col_iter = reversed(col_iter)
for col_pos in col_iter:
tile = row[col_pos]
if tile == SOLID_WALL:
pos = row_pos, col_pos
if reverse:
offset = -1
else:
offset = 1
for axis in ROW, COLUMN:
next_pos = list(pos)
next_pos[axis] += offset
try:
next_tile = get_tile(tiles, next_pos)
except IndexError:
next_tile = None
if next_tile != SOLID_WALL:
raise WallOutsideRoomError(row_pos, col_pos)
return pos
def identify_tile(tile):
"""
Returns a symbolic value for every identified tile or None.
Keyword arguments:
tile --- the tile to identify
"""
#Safely lookup the tile
try:
#Return known tiles
return TILES[tile]
#Handle unknown tiles
except KeyError:
#Return a default value
return
def main():
"""
Takes a board from STDIN and either returns a result to STDOUT or an
error to STDERR.
Called when this file is run on the command line.
"""
#As this function is the only one to use this module, and it can only be
#called once in this configuration, it makes sense to only import it here.
import sys
#Reads the board from standard input.
board = sys.stdin.read()
#Safely handles outside input
try:
#Calculates the result of shooting the laser
result = shoot_laser(board)
#Handles multiple item errors
except (MultipleLaserError, MultipleTargetError) as error:
#Display the error
sys.stderr.write("%s\n" % str(error))
#Loop through all the duplicated item symbols
for symbol in error.symbols:
#Highlight each symbol in green
board = board.replace(symbol, "\033[01;31m%s\033[m" % symbol)
#Display the board
sys.stderr.write("%s\n" % board)
#Exit with an error signal
sys.exit(1)
#Handles item missing errors
except (NoLaserError, NoTargetError) as error:
#Display the error
sys.stderr.write("%s\n" % str(error))
#Display the board
sys.stderr.write("%s\n" % board)
#Exit with an error signal
sys.exit(1)
#Handles errors caused by symbols
except (OutsideRoomError, WallNotRectangleError) as error:
#Displays the error
sys.stderr.write("%s\n" % str(error))
lines = board.split("\n")
line = lines[error.row_pos]
before = line[:error.col_pos]
after = line[error.col_pos + 1:]
symbol = line[error.col_pos]
line = "%s\033[01;31m%s\033[m%s" % (before, symbol, after)
lines[error.row_pos] = line
board = "\n".join(lines)
#Display the board
sys.stderr.write("%s\n" % board)
#Exit with an error signal
sys.exit(1)
#Handles errors caused by non-solid walls
except WallNotSolidError as error:
#Displays the error
sys.stderr.write("%s\n" % str(error))
lines = board.split("\n")
line = lines[error.row_pos]
before = line[:error.col_pos]
after = line[error.col_pos + 1:]
symbol = line[error.col_pos]
line = "%s\033[01;5;31m#\033[m%s" % (before, after)
lines[error.row_pos] = line
board = "\n".join(lines)
#Display the board
sys.stderr.write("%s\n" % board)
#Exit with an error signal
sys.exit(1)
#If a result was returned
else:
#Converts the result into a string
result_str = str(result)
#Makes the string lowercase
lower_result = result_str.lower()
#Returns the result
sys.stdout.write("%s\n" % lower_result)
def parse_board(board):
"""
Interprets the raw board syntax and returns a grid of tiles.
Keyword arguments:
board --- the board containing the tiles (walls, laser, target, etc)
"""
#Create a container for all the lines
tiles = list()
#Loop through all the lines of the board
for line in board.split("\n"):
#Identify all the tiles on the line
row = [identify_tile(tile) for tile in line]
#Add the row to the container
tiles.append(row)
#Return the container
return tiles
def reflect(mirror, direction):
"""
Returns an updated laser direction after it has been reflected on a
mirror.
Keyword arguments:
mirror --- the mirror to reflect the laser from
direction --- the direction the laser is travelling in
"""
try:
direction_lookup = REFLECTIONS[mirror]
except KeyError:
raise TypeError("%s is not a mirror.", mirror)
try:
return direction_lookup[direction]
except KeyError:
raise TypeError("%s is not a direction.", direction)
def shoot_laser(board):
"""
Shoots the boards laser and returns whether it will hit the target.
Keyword arguments:
board --- the board containing the tiles (walls, laser, target, etc)
"""
tiles = parse_board(board)
validate_board(tiles)
return does_laser_hit_target(tiles)
def validate_board(tiles):
"""
Checks an board to see if it is valid and raises an exception if not.
Keyword arguments:
tiles --- row/column based version of a board containing symbolic
versions of the tiles (walls, laser, target, etc)
"""
found_laser = False
found_target = False
try:
n_wall, w_wall = get_wall_pos(tiles)
s_wall, e_wall = get_wall_pos(tiles, reverse=True)
except TypeError:
n_wall = e_wall = s_wall = w_wall = None
number_of_rows = len(tiles)
for row_pos in range(number_of_rows):
row = tiles[row_pos]
number_of_cols = len(row)
for col_pos in range(number_of_cols):
tile = row[col_pos]
if ((row_pos in (n_wall, s_wall) and
col_pos in range(w_wall, e_wall))
or
(col_pos in (e_wall, w_wall) and
row_pos in range(n_wall, s_wall))):
if tile != SOLID_WALL:
raise WallNotSolidError(row_pos, col_pos)
elif (n_wall != None and
(row_pos < n_wall or
col_pos > e_wall or
row_pos > s_wall or
col_pos < w_wall)):
if tile in LASERS:
raise LaserOutsideRoomError(row_pos, col_pos)
elif tile == TARGET:
raise TargetOutsideRoomError(row_pos, col_pos)
elif tile == SOLID_WALL:
if not (row_pos >= n_wall and
col_pos <= e_wall and
row_pos <= s_wall and
col_pos >= w_wall):
raise WallOutsideRoomError(row_pos, col_pos)
else:
if tile in LASERS:
if not found_laser:
found_laser = True
else:
raise MultipleLaserError(row_pos, col_pos)
elif tile == TARGET:
if not found_target:
found_target = True
else:
raise MultipleTargetError(row_pos, col_pos)
if not found_laser:
raise NoLaserError(tiles)
if not found_target:
raise NoTargetError(tiles)
class LasersError(Exception):
"""Parent Error Class for all errors raised."""
pass
class NoLaserError(LasersError):
"""Indicates that there are no lasers on the board."""
symbols = "^v><"
def __str__ (self):
return "No laser (%s) to fire." % ", ".join(self.symbols)
class NoTargetError(LasersError):
"""Indicates that there are no targets on the board."""
symbols = "x"
def __str__ (self):
return "No target (%s) to hit." % ", ".join(self.symbols)
class MultipleLaserError(LasersError):
"""Indicates that there is more than one laser on the board."""
symbols = "^v><"
def __str__ (self):
return "Too many lasers (%s) to fire, only one is allowed." % \
", ".join(self.symbols)
class MultipleTargetError(LasersError):
"""Indicates that there is more than one target on the board."""
symbols = "x"
def __str__ (self):
return "Too many targets (%s) to hit, only one is allowed." % \
", ".join(self.symbols)
class WallNotSolidError(LasersError):
"""Indicates that the perimeter wall is not solid."""
__slots__ = ("__row_pos", "__col_pos", "n_wall", "s_wall", "e_wall",
"w_wall")
def __init__(self, row_pos, col_pos):
self.__row_pos = row_pos
self.__col_pos = col_pos
def __str__ (self):
return "Walls must form a solid rectangle."
def __get_row_pos(self):
return self.__row_pos
def __get_col_pos(self):
return self.__col_pos
row_pos = property(__get_row_pos)
col_pos = property(__get_col_pos)
class WallNotRectangleError(LasersError):
"""Indicates that the perimeter wall is not a rectangle."""
__slots__ = ("__row_pos", "__col_pos")
def __init__(self, row_pos, col_pos):
self.__row_pos = row_pos
self.__col_pos = col_pos
def __str__ (self):
return "Walls must form a rectangle."
def __get_row_pos(self):
return self.__row_pos
def __get_col_pos(self):
return self.__col_pos
row_pos = property(__get_row_pos)
col_pos = property(__get_col_pos)
class OutsideRoomError(LasersError):
"""Indicates an item is outside of the perimeter wall."""
__slots__ = ("__row_pos", "__col_pos", "__name")
def __init__(self, row_pos, col_pos, name):
self.__row_pos = row_pos
self.__col_pos = col_pos
self.__name = name
def __str__ (self):
return "A %s was found outside of a 'room'." % self.__name
def __get_row_pos(self):
return self.__row_pos
def __get_col_pos(self):
return self.__col_pos
row_pos = property(__get_row_pos)
col_pos = property(__get_col_pos)
class LaserOutsideRoomError(OutsideRoomError):
"""Indicates the laser is outside of the perimeter wall."""
def __init__ (self, row_pos, col_pos):
OutsideRoomError.__init__(self, row_pos, col_pos, "laser")
class TargetOutsideRoomError(OutsideRoomError):
"""Indicates the target is outside of the perimeter wall."""
def __init__ (self, row_pos, col_pos):
OutsideRoomError.__init__(self, row_pos, col_pos, "target")
class WallOutsideRoomError(OutsideRoomError):
"""Indicates that there is a wall outside of the perimeter wall."""
def __init__ (self, row_pos, col_pos):
OutsideRoomError.__init__(self, row_pos, col_pos, "wall")
if __name__ == "__main__":
main()
A bash script to show off the colour error reporting:
#!/bin/bash
declare -a TESTS
test() {
echo -e "\033[1m$1\033[0m"
tput sgr0
echo "$2" | ./lasers.py
echo
}
test \
"no laser" \
" ##########
# x #
# / #
# /#
# \\ #
##########"
test \
"multiple lasers" \
" ##########
# v x #
# / #
# /#
# \\ ^ #
##########"
test \
"no target" \
" ##########
# v #
# / #
# /#
# \\ #
##########"
test \
"multiple targets" \
" ##########
# v x #
# / #
# /#
# \\ x #
##########"
test \
"wall not solid" \
" ##### ####
# v x #
# / #
# /#
# \\ #
##########"
test \
"laser_outside_room" \
" ##########
> # x #
# / #
# /#
# \\ #
##########"
test \
"laser before room" \
" > ##########
# x #
# / #
# /#
# \\ #
##########"
test \
"laser row before room" \
" >
##########
# x #
# / #
# /#
# \\ #
##########"
test \
"laser after room" \
" ##########
# x #
# / #
# /#
# \\ #
########## >"
test \
"laser row after room" \
" ##########
# x #
# / #
# /#
# \\ #
##########
> "
test \
"target outside room" \
" ##########
x # v #
# / #
# /#
# \\ #
##########"
test \
"target before room" \
" x ##########
# v #
# / #
# /#
# \\ #
##########"
test \
"target row before room" \
" x
##########
# v #
# / #
# /#
# \\ #
##########"
test \
"target after room" \
" ##########
# v #
# / #
# /#
# \\ #
########## x"
test \
"target row after room" \
" ##########
# v #
# / #
# /#
# \\ #
##########
x "
test \
"wall outside room" \
" ##########
# # v #
# / #
# /#
# \\ x #
##########"
test \
"wall before room" \
" # ##########
# v #
# / #
# /#
# \\ x #
##########"
test \
"wall row before room" \
" #
##########
# v #
# / #
# /#
# \\ x #
##########"
test \
"wall after room" \
" ##########
# v #
# / #
# /#
# \\ x #
########## #"
test \
"wall row after room" \
" ##########
# v #
# / #
# /#
# \\ x #
##########
#"
test \
"mirror outside room positive" \
" ##########
/ # / \\ #
# #
# \\ x#
# > / #
########## "
test \
"mirrors outside room negative" \
" ##########
\\ # v x #
# / #
# /#
# \\ #
##########"
test \
"mirror before room positive" \
" \\ ##########
# / \\ #
# #
# \\ x#
# > / #
########## "
test \
"mirrors before room negative" \
" / ##########
# v x #
# / #
# /#
# \\ #
##########"
test \
"mirror row before room positive" \
" \\
##########
# / \\ #
# #
# \\ x#
# > / #
########## "
test \
"mirrors row before room negative" \
" \\
##########
# v x #
# / #
# /#
# \\ #
##########"
test \
"mirror after row positive" \
" ##########
# / \\ #
# #
# \\ x#
# > / #
########## / "
test \
"mirrors after row negative" \
" ##########
# v x #
# / #
# /#
# \\ #
########## / "
test \
"mirror row after row positive" \
" ##########
# / \\ #
# #
# \\ x#
# > / #
##########
/ "
test \
"mirrors row after row negative" \
" ##########
# v x #
# / #
# /#
# \\ #
##########
/ "
test \
"laser hitting laser" \
" ##########
# v \\#
# #
# #
#x \\ /#
##########"
test \
"mirrors positive" \
" ##########
# / \\ #
# #
# \\ x#
# > / #
########## "
test \
"mirrors negative" \
" ##########
# v x #
# / #
# /#
# \\ #
##########"
test \
"wall collision" \
" #############
# # #
# > # #
# # #
# # x #
# # #
#############"
test \
"extreme example" \
" ##########
#/\\/\\/\\ #
#\\\\//\\\\\\ #
#//\\/\\/\\\\#
#\\/\\/\\/x^#
##########"
test \
"brian example 1" \
"##########
# / \\ #
# #
#/ \\ x#
#\\> / #
##########"
test \
"brian example 2" \
"##########
# / \\#
# / \\ #
#/ \\ x#
#\\^/\\ / #
##########"
The unittests used in development:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import unittest
from lasers import *
class TestTileRecognition(unittest.TestCase):
def test_solid_wall(self):
self.assertEqual(SOLID_WALL, identify_tile("#"))
def test_target(self):
self.assertEqual(TARGET, identify_tile("x"))
def test_mirror_ne_sw(self):
self.assertEqual(MIRROR_NE_SW, identify_tile("/"))
def test_mirror_nw_se(self):
self.assertEqual(MIRROR_NW_SE, identify_tile("\\"))
def test_laser_down(self):
self.assertEqual(LASER_DOWN, identify_tile("v"))
def test_laser_up(self):
self.assertEqual(LASER_UP, identify_tile("^"))
def test_laser_right(self):
self.assertEqual(LASER_RIGHT, identify_tile(">"))
def test_laser_left(self):
self.assertEqual(LASER_LEFT, identify_tile("<"))
def test_other(self):
self.assertEqual(None, identify_tile(" "))
class TestReflection(unittest.TestCase):
def setUp(self):
self.DIRECTION = LEFT
self.NOT_DIRECTIO
Ruby, 176 characters
x=!0;y=0;e="^v<>#x";b=readlines;b.map{|l|(x||=l=~/[v^<>]/)||y+=1};c=e.index(b[y][x])
loop{c<2&&y+=c*2-1;c>1&&x+=2*c-5;e.index(n=b[y][x])&&(p n==?x;exit);c^=' \/'.index(n)||0}
I used a simple state machine (like most posters), nothing fancy. I just kept whittling it down using every trick I could think of. The bitwise XOR used to change direction (stored as an integer in the variable c) was a big improvement over the conditionals I had in earlier versions.
I have a suspicion that the code that increments x and y could be made shorter. Here is the section of the code that does the incrementing:
c<2&&y+=c*2-1;c>1&&x+=(c-2)*2-1
Edit: I was able to shorten the above slightly:
c<2&&y+=c*2-1;c>1&&x+=2*c-5
The current direction of the laser c is stored as follows:
0 => up
1 => down
2 => left
3 => right
The code relies on this fact to increment x and y by the correct amount (0, 1, or -1). I tried rearranging which numbers map to each direction, looking for an arrangement that would let me do some bitwise manipulation to increment the values, because I have a nagging feeling that it would be shorter than the arithmetic version.
C# 3.0
259 chars
bool S(char[]m){var w=Array.FindIndex(m,x=>x<11)+1;var s=Array.FindIndex(m,x=>x>50&x!=92&x<119);var t=m[s];var d=t<61?-1:t<63?1:t<95?-w:w;var u=0;while(0<1){s+=d;u=m[s];if(u>119)return 0<1;if(u==47|u==92)d+=d>0?-w-1:w+1;else if(u!=32)return 0>1;d=u>47?-d:d;}}
Slightly more readable:
bool Simulate(char[] m)
{
var w = Array.FindIndex(m, x => x < 11) + 1;
var s = Array.FindIndex(m, x => x > 50 & x != 92 & x < 119);
var t = m[s];
var d = t < 61 ? -1 : t < 63 ? 1 : t < 95 ? -w : w;
var u = 0;
while (0 < 1)
{
s += d;
u = m[s];
if (u > 119)
return 0 < 1;
if (u == 47 | u == 92)
d += d > 0 ? -w - 1 : w + 1;
else if (u != 32)
return 0 > 1;
d = u > 47 ? -d : d;
}
}
The main waste of chars seems to be in finding the width of the map and the position of the laser source. Any ideas how to shorten this?
C + ASCII, 197 characters:
G[999],*p=G,w,z,t,*b;main(){for(;(*p++=t=getchar()^32)>=0;w=w|t-42?w:p-G)z=t^86?t^126?t^28?t^30?z:55:68:56:75,b=z?b:p;for(;t=z^55?z^68?z^56?z^75?0:w:-w:-1:1;z^=*b)b+=t;puts(*b^88?"false":"true");}
This C solution assumes an ASCII character set, allowing us to use the XOR mirror trick. It's also incredibly fragile - all the input lines must be the same length, for example.
It breaks under the 200 character mark - but dang it, still haven't beaten those Perl solutions!
Golfscript (83 characters)
Hello, gnibbler!
:\'><v^'.{\?}%{)}?:P#=?{:O[1-1\10?).~)]=P+
:P\=' \/x'?[O.2^.1^'true''false']=.4/!}do
Python - 152
Reads input from a file called "L"
A=open("L").read()
W=A.find('\n')+1
D=P=-1
while P<0:D+=1;P=A.find(">^<v"[D])
while D<4:P+=[1,-W,-1,W][D];D=[D,D^3,D^1,4,5][' \/x'.find(A[P])]
print D<5
To read from stdin replace the first line with this
import os;A=os.read(0,1e9)
If you need lowercase true/false change the last line to
print`D<5`.lower()
JavaScript - 265 Characters
Update IV - Odds are this will be the last round of updates, managed to save a couple more characters by switching to a do-while loop and rewriting the movement equation.
Update III - Thanks to the suggestion by strager in regards to removing Math.abs() and putting the variables in the global name space, that coupled with some rearranging of the variable assignments got the code down to 282 characters.
Update II - Some more updates to the code to remove the use of != -1 as well as some better use of variables for longer operations.
Update - When through and made some changes by creating a reference to the indexOf function (thanks LiraNuna!) and removing parenthesis that were not needed.
This is my first time doing a code golf so I'm not sure how much better this could be, any feed back is appreciated.
Fully minimized version:
a;b;c;d;e;function f(g){a=function(a){return g.indexOf(a)};b=a("\n")+1;a=g[c=e=a("v")>0?e:e=a("^")>0?e:e=a("<")>0?e:a(">")];d=a=="<"?-1:a==">"?1:a=="^"?-b:b;do{e=d==-1|d==1;a=g[c+=d=a=="\\"?e?b*d:d>0?1:-1:a=="/"?e?-b*d:d>0?1:-1:d];e=a=="x"}while(a!="#"^e);return e}
Original version with comments:
character; length; loc; movement; temp;
function checkMaze(maze) {
// Use a shorter indexOf function
character = function(string) { return maze.indexOf(string); }
// Get the length of the maze
length = character("\n") + 1;
// Get the location of the laser in the string
character = maze[loc = temp = character("v") > 0 ? temp :
temp = character("^") > 0 ? temp :
temp = character("<") > 0 ? temp : character(">")];
// Get the intial direction that we should travel
movement = character == "<" ? -1 :
character == ">" ? 1 :
character == "^" ? -length : length;
// Move along until we reach the end
do {
// Get the current character
temp = movement == -1 | movement == 1;
character = maze[loc += movement = character == "\\" ? temp ? length * movement : movement > 0 ? 1 : -1 :
character == "/" ? temp ? -length * movement : movement > 0 ? 1 : -1 : movement];
// Have we hit a target?
temp = character == "x";
// Have we hit a wall?
} while (character != "#" ^ temp);
// temp will be false if we hit the target
return temp;
}
Web page to test with:
<html>
<head>
<title>Code Golf - Lasers</title>
<script type="text/javascript">
a;b;c;d;e;function f(g){a=function(a){return g.indexOf(a)};b=a("\n")+1;a=g[c=e=a("v")>0?e:e=a("^")>0?e:e=a("<")>0?e:a(">")];d=a=="<"?-1:a==">"?1:a=="^"?-b:b;do{e=d==-1|d==1;a=g[c+=d=a=="\\"?e?b*d:d>0?1:-1:a=="/"?e?-b*d:d>0?1:-1:d];e=a=="x"}while(a!="#"^e);return e}
</script>
</head>
<body>
<textarea id="maze" rows="10" cols="10"></textarea>
<button id="checkMaze" onclick="alert(f(document.getElementById('maze').value))">Maze</button>
</body>
</html>
House of Mirrors
Not an actual entry to the challenge, but I wrote a game based on this concept (not too long back).
It's written in Scala, open-source and available here:
It does a little bit more; deals with colors and various types of mirrors and devices, but version 0.00001 did exactly what this challenge asks. I have lost that version though and it was never optimised for character count anyway.
c (K&R) 339 necessary characters after more suggestions from strager.
The physicist in me noted that the propagation and reflection operations are time-reversal invariant, so this version, throws rays from the target and checks to see if the arrive at the laser emitter.
The rest of the implementation is very straight forward and is taken more or less exactly from my earlier, forward going effort.
Compressed:
#define R return
#define C case
#define Z x,y
int c,i,j,m[99][99],Z;s(d,e,Z){for(;;)switch(m[x+=d][y+=e]){C'^':R 1==e;
C'>':R-1==d;C'v':R-1==e;C'<':R 1==d;C'#':C'x':R 0;C 92:e=-e;d=-d;C'/':c=d;
d=-e;e=-c;}}main(){while((c=getchar())>0)c==10?i=0,j++:(c==120?x=i,y=j:
i,m[i++][j]=c);puts(s(1,0,Z)|s(0,1,Z)|s(-1,0,Z)|s(0,-1,Z)?"true":"false");}
Uncompressed(ish):
#define R return
#define C case
#define Z x,y
int c,i,j,m[99][99],Z;
s(d,e,Z)
{
for(;;)
switch(m[x+=d][y+=e]){
C'^':
R 1==e;
C'>':
R-1==d;
C'v':
R-1==e;
C'<':
R 1==d;
C'#':
C'x':
R 0;
C 92:
e=-e;
d=-d;
C'/':
c=d;
d=-e;
e=-c;
}
}
main(){
while((c=getchar())>0)
c==10?i=0,j++:
(c==120?x=i,y=j:i,m[i++][j]=c);
puts(s(1,0,Z)|s(0,1,Z)|s(-1,0,Z)|s(0,-1,Z)?"true":"false");
}
There is no input validation, and bad input can send it into an infinite loop. Works properly with input no larger than 99 by 99. Requires a compiler that will link the standard library without including any of the headers. And I think I'm done, strager has me beat by a considerable stretch, even with his help.
I'm rather hoping someone will demonstrate a more subtle way to accomplish the task. There s nothing wrong with this, but it is not deep magic.
Ruby - 146 Chars
A=$<.read
W=A.index('
')+1
until
q=A.index(">^<v"[d=d ?d+1:0])
end
while d<4
d=[d,d^3,d^1,4,5][(' \/x'.index(A[q+=[1,-W,-1,W][d]])or 4)]
end
p 5>d
PostScript, 359 bytes
First attempt, lots of room for improvement...
/a[{(%stdin)(r)file 99 string readline not{exit}if}loop]def a{{[(^)(>)(<)(v)]{2
copy search{stop}if pop pop}forall}forall}stopped/r count 7 sub def pop
length/c exch def[(>)0(^)1(<)2(v)3>>exch get/d exch def{/r r[0 -1 0 1]d get
add def/c c[1 0 -1 0]d get add def[32 0 47 1 92 3>>a r get c get .knownget
not{exit}if/d exch d xor def}loop a r get c get 120 eq =
Haskell, 395 391 383 361 339 characters (optimized)
Still uses a generic state machine, rather than anything clever:
k="<>^v"
o(Just x)=x
s y(h:t)=case b of{[]->s(y+1)t;(c:_)->(c,length a,y)}where(a,b)=break(flip elem k)h
r a = f$s 0 a where f(c,x,y)=case i(a!!v!!u)"x /\\"["true",g k,g"v^><",g"^v<>"]of{Just r->r;_->"false"}where{i x y=lookup x.zip y;j=o.i c k;u=j[x-1,x+1,x,x];v=j[y,y,y-1,y+1];g t=f(j t,u,v)}
main=do{z<-getContents;putStrLn$r$lines z}
A readable version:
k="<>^v" -- "key" for direction
o(Just x)=x -- "only" handle successful search
s y(h:t)=case b of -- find "start" state
[]->s(y+1)t
(c:_)->(c,length a,y)
where (a,b)=break(flip elem k)h
r a = f$s 0 a where -- "run" the state machine (iterate with f)
f(c,x,y)=case i(a!!v!!u)"x /\\"["true",g k,g"v^><",g"^v<>"] of
Just r->r
_->"false"
where
i x y=lookup x.zip y -- "index" with x using y as key
j=o.i c k -- use c as index k as key; assume success
u=j[x-1,x+1,x,x] -- new x coord
v=j[y,y,y-1,y+1] -- new y coord
g t=f(j t,u,v) -- recurse; use t for new direction
main=do
z<-getContents
putStrLn$r$lines z
I believe in Code Reuse, I'd use one of your code as an API :).
puts Board.new.validate(input)
32 characters \o/... wohoooo
C++: 388 characters
#include<iostream>
#include<string>
#include<deque>
#include<cstring>
#define w v[y][x]
using namespace std;size_t y,x,*z[]={&y,&x};int main(){string p="^v<>",s;deque<string>v;
while(getline(cin,s))v.push_back(s);while(x=v[++y].find_first_of(p),!(x+1));int
i=p.find(w),d=i%2*2-1,r=i/2;do while(*z[r]+=d,w=='/'?d=-d,0:w==' ');while(r=!r,
!strchr("#x<^v>",w));cout<<(w=='x'?"true":"false");}
(318 without headers)
How it works:
First, all lines are read in, then, the laser is found. The following will evaluate to 0 as long as no laser arrow was found yet, and the same time assign to x the horizontal position.
x=v[++y].find_first_of(p),!(x+1)
Then we look what direction we found and store it in i. Even values of i are top/left ("decreasing") and odd values are bottom/right ("increasing"). According to that notion, d ("direction") and r ("orientation") are set. We index pointer array z with orientation and add the direction to the integer we get. The direction changes only if we hit a slash, while it remains the same when we hit a back-slash. Of course, when we hit a mirror, then we always change orientation (r = !r).
Groovy # 279 characers
m=/[<>^v]/
i={'><v^'.indexOf(it)}
n=['<':{y--},'>':{y++},'^':{x--},'v':{x++}]
a=['x':{1},'\\':{'v^><'[i(d)]},'/':{'^v<>'[i(d)]},'#':{},' ':{d}]
b=[]
System.in.eachLine {b<<it.inject([]) {r,c->if(c==~m){x=b.size;y=r.size;d=c};r<<c}}
while(d==~m){n[d]();d=a[b[x][y]]()}
println !!d
C#
1020 characters.
1088 characters (added input from console).
925 characters (refactored variables).
875 characters (removed redundant Dictionary initializer; changed to Binary & operators)
Made a Point not to look at anyone else's before posting. I'm sure it could be LINQ'd up a bit. And the whole FindLaser method in the readable version seems awfully fishy to me. But, it works and it's late :)
Note the readable class includes an additional method that prints out the current Arena as the laser moves around.
class L{static void Main(){
A=new Dictionary<Point,string>();
var l=Console.ReadLine();int y=0;
while(l!=""){var a=l.ToCharArray();
for(int x=0;x<a.Count();x++)
A.Add(new Point(x,y),l[x].ToString());
y++;l=Console.ReadLine();}new L();}
static Dictionary<Point,string>A;Point P,O,N,S,W,E;
public L(){N=S=W=E=new Point(0,-1);S.Offset(0,2);
W.Offset(-1,1);E.Offset(1,1);D();
Console.WriteLine(F());}bool F(){
var l=A[P];int m=O.X,n=O.Y,o=P.X,p=P.Y;
bool x=o==m,y=p==n,a=x&p<n,b=x&p>n,c=y&o>m,d=y&o<m;
if(l=="\\"){if(a)T(W);if(b)T(E);if(c)T(S);
if(d)T(N);if(F())return true;}
if(l=="/"){if(a)T(E);if(b)T(W);if(c)T(N);
if(d)T(S);if(F())return true;}return l=="x";}
void T(Point p){O=P;do P.Offset(p);
while(!("\\,/,#,x".Split(',')).Contains(A[P]));}
void D(){P=A.Where(x=>("^,v,>,<".Split(',')).
Contains(x.Value)).First().Key;var c=A[P];
if(c=="^")T(N);if(c=="v")T(S);if(c=="<")T(W);
if(c==">")T(E);}}
Readable Version (not quite the final golf version, but same premise):
class Laser
{
private Dictionary<Point, string> Arena;
private readonly List<string> LaserChars;
private readonly List<string> OtherChars;
private Point Position;
private Point OldPosition;
private readonly Point North;
private readonly Point South;
private readonly Point West;
private readonly Point East;
public Laser( List<string> arena )
{
SplitArena( arena );
LaserChars = new List<string> { "^", "v", ">", "<" };
OtherChars = new List<string> { "\\", "/", "#", "x" };
North = new Point( 0, -1 );
South = new Point( 0, 1 );
West = new Point( -1, 0 );
East = new Point( 1, 0 );
FindLaser();
Console.WriteLine( FindTarget() );
}
private void SplitArena( List<string> arena )
{
Arena = new Dictionary<Point, string>();
int y = 0;
foreach( string str in arena )
{
var line = str.ToCharArray();
for( int x = 0; x < line.Count(); x++ )
{
Arena.Add( new Point( x, y ), line[x].ToString() );
}
y++;
}
}
private void DrawArena()
{
Console.Clear();
var d = new Dictionary<Point, string>( Arena );
d[Position] = "*";
foreach( KeyValuePair<Point, string> p in d )
{
if( p.Key.X == 0 )
Console.WriteLine();
Console.Write( p.Value );
}
System.Threading.Thread.Sleep( 400 );
}
private bool FindTarget()
{
DrawArena();
string chr = Arena[Position];
switch( chr )
{
case "\\":
if( ( Position.X == Position.X ) && ( Position.Y < OldPosition.Y ) )
{
OffSet( West );
}
else if( ( Position.X == Position.X ) && ( Position.Y > OldPosition.Y ) )
{
OffSet( East );
}
else if( ( Position.Y == Position.Y ) && ( Position.X > OldPosition.X ) )
{
OffSet( South );
}
else
{
OffSet( North );
}
if( FindTarget() )
{
return true;
}
break;
case "/":
if( ( Position.X == Position.X ) && ( Position.Y < OldPosition.Y ) )
{
OffSet( East );
}
else if( ( Position.X == Position.X ) && ( Position.Y > OldPosition.Y ) )
{
OffSet( West );
}
else if( ( Position.Y == Position.Y ) && ( Position.X > OldPosition.X ) )
{
OffSet( North );
}
else
{
OffSet( South );
}
if( FindTarget() )
{
return true;
}
break;
case "x":
return true;
case "#":
return false;
}
return false;
}
private void OffSet( Point p )
{
OldPosition = Position;
do
{
Position.Offset( p );
} while( !OtherChars.Contains( Arena[Position] ) );
}
private void FindLaser()
{
Position = Arena.Where( x => LaserChars.Contains( x.Value ) ).First().Key;
switch( Arena[Position] )
{
case "^":
OffSet( North );
break;
case "v":
OffSet( South );
break;
case "<":
OffSet( West );
break;
case ">":
OffSet( East );
break;
}
}
}
Perl 219
My perl version is 392 342 characters long (I had to handle the case of the beam hitting the laser):
Update, thanks Hobbs for reminding me of tr//, it's now 250 characters:
Update, removing the m in m//, changing the two while loops brought a few savings; there's now only one space required.
(L:it;goto L is the same length as do{it;redo}):
#b=map{($y,$x,$s)=($a,$-[0],$&)if/[<>^v]/;$a++;[split//]}<>;L:$_=$s;$x++if/>/;
$x--if/</;$y++if/v/;$y--if/\^/;$_=$b[$y][$x];die"true\n"if/x/;die"false\n"if
/[<>^v#]/;$s=~tr/<>^v/^v<>/if/\\/;$s=~tr/<>^v/v^></if/\//;goto L
I shaved some, but it barelyjust competes with some of these, albeit late.
It looks a little better as:
#!/usr/bin/perl
#b = map {
($y, $x, $s) = ($a, $-[0], $&) if /[<>^v]/;
$a++;
[split//]
} <>;
L:
$_ = $s;
$x++ if />/;
$x-- if /</;
$y++ if /v/;
$y-- if /\^/;
$_ = $b[$y][$x];
die "true\n" if /x/;
die "false\n" if /[<>^v#]/;
$s =~ tr/<>^v/^v<>/ if /\\/;
$s =~ tr/<>^v/v^></ if /\//;
goto L
Well... Honestly this should be self explanatory if you understand that the #b is an array arrays of characters in each line, and can read the simple regexp and tr statements.
F# - 454 (or thereabouts)
Bit late to the game, but can't resist posting my 2d attempt.
Update modified slightly. Now stops correctly if transmitter is hit. Pinched Brian's idea for IndexOfAny (shame that line is so verbose). I haven't actually managed to work out how to get ReadToEnd to return from the Console, so I'm taking that bit on trust...
I'm pleased with this answer, as though it is quite short, it is still fairly readable.
let s=System.Console.In.ReadToEnd() //(Not sure how to get this to work!)
let w=s.IndexOf('\n')+1 //width
let h=(s.Length+1)/w //height
//wodge into a 2d array
let a=Microsoft.FSharp.Collections.Array2D.init h (w-1)(fun y x -> s.[y*w+x])
let p=s.IndexOfAny[|'^';'<';'>';'v'|] //get start pos
let (dx,dy)= //get initial direction
match "^<>v".IndexOf(s.[p]) with
|0->(0,-1)
|1->(-1,0)
|2->(1,0)
|_->(0,1)
let mutable(x,y)=(p%w,p/w) //translate into x,y coords
let rec f(dx,dy)=
x<-x+dx;y<-y+dy //mutate coords on each call
match a.[y,x] with
|' '->f(dx,dy) //keep going same direction
|'/'->f(-dy,-dx) //switch dx/dy and change sign
|'\\'->f(dy,dx) //switch dx/dy and keep sign
|'x'->"true"
|_->"false"
System.Console.Write(f(dx,dy))