Hey! You made ImpactJS? That's very cool, but it's really hard to commit $99 to buy an engine when I can't even try it! It looks super-rad and I'd love to give it a shot, but it's a bit high of a bar for a casual game dev.
If you buy it and don't like it for whatever reason, I can give you a full refund within the first month. That's easier for me to handle than a separate trial license.
- not being able to see the class of something. Its more ruby style to do 'space.is_a?(Enemy)' than 'space.enemy?'. This would also help to build case statements rather than lots of ifs.
- no 'print' or 'puts'. How am I supposed to know what 'look' returns when it never really tells me?
- I prefer a single set of rules rather than adding rules as you go. When first I have to use :backward but later this becomes basically redundant as 'pivot' is added, it just makes me frustrated in having to refactor. Same thing for feel/look.
I don't feel `.is_a?(Class)` is ever idiomatic ruby. You should always be coding to an interface, not an implementation. This prevents things like mocks and duck typing, making testing more difficult and adding complexity when you want to add AnotherEnemy that doesn't share the same inheritance tree.
I try to avoid using `.is_a?(Class)` in all of my ruby code when possible. `.respond_to?` is much more responsible way of probing whether an object has the interface you are looking for.
> I try to avoid using `.is_a?(Class)` in all of my ruby code when possible. `.respond_to?` is much more responsible way of probing whether an object has the interface you are looking for.
#is_a?(class) is a much more specific check of semantics than #respond_to?(method_name). is_a? is the more cautious approach (least likely to produce false positives at the expense of being most likely to produce false negatives) whereas respond_to? is the less cautious approach (least likely to produce false negatives while most likely to produce false positives.)
I am well aware of duck typing, and rarely use either #is_a? or #respond_to?
That doesn't change that, if you have a reason to use that type of inquiry to check interfaces, #is_a? is the more safe of the two (more likely to reject an object that provides the semantics of concern and less likely to accept one that does not) and #respond_to? is the less safe (more likely to accept an object that does not provide the semantics of concern and less likely to reject one that does.)
Hehe, i feel identified with some of these frustrations, but i think it's better the way it is.
> Its more ruby style to do 'space.is_a?(Enemy)' than 'space.enemy?'. This would also help to build case statements rather than lots of ifs.
But that would require to explain that an Enemy class/module (and possibly others) exists. With the presented interface you can/should only care about Warrior and Place. Much simpler.
> no 'print' or 'puts'. How am I supposed to know what 'look' returns when it never really tells me?
With Ruby's Array methods. Warrior#look returns an Array of at most 3 Places. What else should you know?
> I prefer a single set of rules rather than adding rules as you go. When first I have to use :backward but later this becomes basically redundant as 'pivot' is added, it just makes me frustrated in having to refactor. Same thing for feel/look.
That happened to me too, but i think it adds to the sense of progress and discovery throughout the game. Like most games, you don't start with all the tools and abilities at your disposal. And you can always ask respond_to? if you want an algorithm that works for all level ;)
Not just to you but to other comments too on the 'is_a?' issue:
The basic issue is that this is supposed to be a 'learn ruby' tool but rather than use the ruby class system (a fundamental thing to learn for beginners) to define what is in a Place they have used a custom non-class-based system instead. The result of this is that rather than allowing the user to choose their own way to do things (remembering that ruby is a 'there is more than one way to do things' language), it prescribes certain ways of working (or at least makes them easier than others).
I'm not saying everyone should program the way I do but I want to write a simple 'case warrior.feel; when Enemy;x; when Stairs;y; when Empty...' statement which is usually pretty clear and logical ruby but is prevented by this structure.
Although you're right that the case statement looks a lot nicer (though ```case; when warrior.enemy?;x; when warrior.stairs?;y...``` is really not much worse if you really want a case statement), I'm going to have to join the chorus in vehemently disagreeing that testing the type is in any way idiomatic ruby.
#is_a? is generally considered a code smell in Ruby. It should not matter if the object is of type Enemy, it responding to the message #enemy? is an interface that any object of any type can implement.
- Not sure why they chose to disallow them here, but realistically #while loops are not commonly used in Ruby. Iterators such as #each or #map are far more idiomatic for Ruby. I still agree this is strange that they are not included here. The original allows all of Ruby https://github.com/ryanb/ruby-warrior
- Obfuscating the class is intentional... you are being given a limited set of "sensors" with which to interact with the world. Those are intentional rules by which to play the game.
- The original game is plain old Ruby run in the terminal and has full access to #puts or #print
- I think the point is to increase the complexity of the api slowly for those new to ruby and or programming in general. Not to beat a dead horse, but in the original there is the notion of epic mode where you do have access to all the rules and can attempt to run the entire tower with the same code.
No loops because (as it's a game AI) it's meant to be called once per game tick, rather than as a driver. I didn't try continuations but it's possible you could get something like that to work.
As a Rubyist for a decade now, please avoid is_a? if at all possible. It defeats the awesome flexibility that is duck typing. Also, I have no idea why you feel that would be better for cases, my code ran in a case statement for brevity.
'look' told me that it returns an array of Spaces. They're the same type as the Space feel returns.
def play_turn(warrior)
m = warrior.methods.grep(/.!$/).sample
warrior.send(m, *([nil, :backward].sample if m != :rest!))
end
(that sucker of :rest! that doesn't accept a direction argument!)
It's kind of amusing to see the warrior do random stuff like shooting arrows backwards and pivoting back in the worst possible moments. Too bad that Run button repeats the same movements if you didn't edit the source code somehow.
No, it seems to be a client-side thing. If no edit was made in the code editor since last run it doesn't make the request to https://www.bloc.io/api/v1/ruby_warrior_players/:id (which seems to be the one that returns the warrior and enemy movements). Repeating the same request in cURL multiple times does return different results.
I am really disappointed that Ryan Bates is not being STRONGLY credited anywhere on this page... this is just a graphical wrapper on his original creation.
Feature request: After you've beaten it once let you go through all levels with the same code. Right now it doesn't let you use the features it hasn't told you about.
As someone who has never done any development in Ruby. I loved the idea and wanted to use. However what makes it not accessible is that you give no instruction initially on the structure of Ruby syntax. This means that I would need to follow another book or tutorial. Please consider drip feeding the player a syntax cheat sheet that will help with each level .
Oh goddamn, this is amazing. As someone who hasn't even seen Ruby code before, you could include a bit more on the syntax of conditional/loop constructs (if/while/etc), but it's amazing. I'll send this to my non-programmer friends right now.
I have to admit I kind of like that it doesn't. All games have artificial constraints and I feel like keeping you from inspecting too much of the internal state is a reasonable one for this.
I would like it to debug my own code. You are more aware of the artificial nature of the variables you create, so it’s not as world-breaking to inspect their contents.
I saved a `health_change` variable in my code, and decided what to do based on that, but the warrior did the wrong thing. I wanted to see if my variable was being set correctly by printing it each turn, but I couldn’t. My normal tool for understanding what was going on is missing, so I was forced to guess at the bug. I finally got it to work by reversing the order of two terms in my formula for `health_change`, but it was a lot harder to understand what was going on when I could only guess at or simulate the values of the variables.
Cool idea, but I don't think it makes sense for there to be both a Player class and a warrior object. They seem like they are/should be one in the same. At the very least, I think behind the scenes, the Player class should look more like this such that the warrior object is always available, and I don't need to pass it around from method to method that I define. e.g.:
class Player
attr_reader :warrior
def initialize(warrior)
@warrior = warrior
end
def play_turn
# I can now access `warrior` here or in any other methods I write
end
end
The player object would represent the current person playing the game, i.e. you. The warrior represents the object on the screen. The player tells the warrior to take actions, so it makes sense that they're different objects.
If there were multiple warriors then your code would no longer make sense, as the control logic would have to be changing the player_instance.warrior object.
> If there were multiple warriors then your code would no longer make sense, as the control logic would have to be changing the player_instance.warrior object.
You need to keep previous state about warrior. E.g. delta health.
This is really great, but it would be even better if it showed where the error it's finding is located. I am getting some weird errors that I can't spot-find.
just play the original: you can use a the text editor of your choice, run in terminal, run with or without animation (nearly instant), and you have full debugging capabilities via puts. Also you get the rogue-like ascii graphics which is the original inspiration for the game.
[1] http://impactjs.com/