Coder Social home page Coder Social logo

hex_math's People

Contributors

leftiness avatar

Stargazers

 avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

Forkers

stevebest

hex_math's Issues

Test len after contains

If I do this:

assert_eq!(set.len(), 1);
assert(set.contains("foo"));

... then the error message is less useful. Something like "0 != 1". Well, yeah. Okay. But why? In most cases, I have other assertions. I'd rather see "set does not contain foo", so make that assertion first.

Move functionality into traits

Instead of always doing foo(point, otherPoint), it would maybe be nice to be able to point.foo(otherPoint) where self is used as the first point reference.

Prism wall strength should be pattern matched

Instead of saying something like strength: i32 = prism.east_wall_strength, do something more like this:

match prism.east {
  Unbreakable => println!("can't break"),
  Breakable(strength) => println!("{}", strength),
}

That does a better job of explaining the idea than setting the strength to -1 or something.

Make a visualizer

Travel() is the easiest function to consider. Distance() isn't so bad. Range() is getting harder. As these functions get more complex, they're harder to visualize. Trying to determine if there is a bug in the logic which draws line of sight will be very difficult without writing some code for visualization.

It doesn't have to be complex. I'm thinking that I could write something which takes an input set of prisms and renders it as ascii art.

fn main() {
  let point: Point = Point::new(1, 2, 5);
  let other: Point = Point::new(3, 4, 5);
  let line: HashSet<Point> = line(&point, &other);

  render(&line):
}

Height differences could be handled in different ways. The easiest would probably be to put a range of height numbers inside the drawn hexes. So it would be drawn from the top down, and a point which is the only drawn point at that QR point would have its QRT coordinate values: (1, 2, 5). The way to convey a coordinate with multiple hexes at different heights would be with QR[T]: (1, 2, [3,4,5]). That's interpreted as an array of QRT values: [ (1, 2, 3), (1, 2, 4), (1, 2,5) ].

I was thinking that eventually I'd like to include images in my docs. Those would be game images eventually. Rendered as proper sprites. In the meantime, maybe I could use something like this ascii visualizer because, again, it's hard to understand a series of point values without a picture.

Field of view

TLDR

  • If a player is standing at a: Point, and b: HashSet<Point> says which points block his vision, and he has a visible range of c: i32, which d: HashSet<Point> can he see?
  • A guard seeks an intruder
  • A wizard shoots a fireball

Notes

While I would first and foremost need a function to find which points are visible, it may also be useful to have a function for determining which pixels are visible. As the resources show, what you choose to show and hide can make for a neat graphical effect.

Resources

Write a function to calculate bearing

Knowing the QRT coordinates of Point A and Point B, calculate the 2D bearing and 3D bearing. Specifically, if A is below B, 2D bearing will not take this into account, but 3D bearing calculation might determine that a line drawn from A to B would go through the bottom of B.

Also impl From<T> for enums::Direction where T is the type of the bearing... maybe f32?

Return HashSet<U> where U: Eq + Hash + From<Point>

Basically, I'll return whatever kind of set you want so long as it works and I know how to convert my points into it. Then you can give me a set of YourObj when you call functions like ray(), and I'll give you those back.

Remove test error From<> in prism.rs

#8 fix will allow adding referenced points. #7 fix will use generic HasValues trait on functions. From<> won't be used, and it's a test failure anyway.

---- prism::Prism_0 stdout ----
    <anon>:9:24: 9:34 error: the trait bound `hex_math::Point: std::convert::From<hex_math::Prism>` is not satisfied [E0277]
<anon>:9     let other: Point = From::from(prism);
                                ^~~~~~~~~~
<anon>:9:24: 9:34 note: required by `std::convert::From::from`
error: aborting due to previous error(s)
thread 'prism::Prism_0' panicked at 'Box<Any>', ../src/librustc/session/mod.rs:161
note: Run with `RUST_BACKTRACE=1` for a backtrace.


failures:
    prism::Prism_0

test result: FAILED. 33 passed; 1 failed; 0 ignored; 0 measured

error: test failed

Go through the docs and add better examples

It's good to have one basic example that demonstrates how the function works (point = 1,2,5; set.contains(point);), but then maybe also write one that's more realistic. You know like when I say "If I fireball is shot at this spot, will the player be in range?" and stuff like that.

The docs right now just say like "calculate the distance between two points." Well, maybe that one is simple enough to not need much more explanation... For example, the doc string for ray is kind of verbose. "Find unblocked points within range in a line through two points."

Maybe I'll eventually even add pictures. :)

Figure out f32 Point

Conflicting thoughts:

  • impl HasValues<i32> for Point<i32>, impl HasValues<f32> for Point<f32>
  • impl HasValues<i32> for Point, impl HasValues<f32> for FloatPoint
  • use HasValues<f32>, FloatPoint::round() -> Point
  • use HasValues<f32>, HasValues<f32>::round() -> Point

At any rate, I want a struct to hold f32 points for use in line(), and I want to get the round() function out of line().

impl From<FooStruct> for Point instead of HasValues

Notes:

It's like... I've got this HasValues<T>. I was thinking... I could
impl From<FooStruct> for Point instead? Do that on the FooStruct file.
Not on the Point file. So you would write this FooStruct in hex_render
or something, and that struct would impl From<FooStruct> for Point.

If I do it that way, I can run inputs of various functions (line,
distance, etc) through From::from() in order to get a Point which I
can use.

It doesn't solve the matter of trying to return an arbitrary
FooStruct. I guess I'll still have to return a Point despite accepting
a FooStruct as an argument. That's not really solved yet, anyway. The
best idea I have is to write a trait that enforces the existence of a
HasUpdate::update(point: &Point) function which would create a new
instance of FooStruct with an updated Point inside.

It's just... I don't really like that I'm doing a bunch of
Point::from(hasValues.values()) all over. fooStruct.into() would be
way nicer. I really should go through my code and see if there are
places where I can use fooStruct.into() instead of
BarStruct::from(fooStruct).

Implement hex-to-pixel, pixel-to-hex

I thought I wouldn't need this, but it's actually needed for #41.

Basically, I have two QRT f32 points. One is in the center of the hex ("round"), and the other is a step along the line ("found"). I want to calculate the angle in order to determine which wall the line will exit on the side. However, (found.q - round.q, found.r - round.r) isn't really the (base, height aka adjacent, opposite) of the triangle on which I can use trig to find the angle.

So I need those XY coordinates from hex-to-pixel. Then I'll be able to use the X and Y difference as the triangle side lengths.

Optimize line take_steps()

My current approach multiplies step_size by number_of_steps and adds that to starting_point in a for loop: round(take_steps(number_of_steps, step_size, starting_point)); take_steps() is a multiplication and an addition for each coordinate QRST for each step.

If I keep track of the previous step that I took, I can round(take_step(previous_step, step_size)). take_step() will be only an addition for each coordinate QRST for each step.

Write Direction.iterator()

Use that instead of impl From<u32> for Direction for iteration. Maybe use the From<u32> to convert from 360 degrees to a direction.

Add to the IsPointMap functions

Writing these flood tests is awful. Ideas for functions:

// Add a point/prism if it doesn't exist, add a wall at that point
map.update_wall(&Point, &Direction, strength)

// Add a point/prism if it doesn't exist, set all walls to unbreakable, set eastern wall on western neighbor as unbreakable as well (etc) because I want nobody to enter or exit this point
map.set_impassable(&Point)

Make ray() aware of walls

I'll need to use a HashMap<Point, HasWalls> instead of a HashSet. Rename opaque to walls. Use #39 to determine which wall would be passed through when I'm on a line from A to B. Then break if I find a point along the line where a strong enough wall exists.

Organize code more consistently

  • Get rid of the utils mod that exists in some files. Make that a range_generic() (or something) (for example)
  • Get things out of the src/ folder and into somewhere else.

Dev blog?

I could probably clean up some of my thoughts on these issues and turn them into posts on idk Medium or something. I'm already writing things down as I work through problems. Is it valuable to keep that somewhere instead of sending it to the void of closed issues?

Make a better get_step_direction()

RIght now, I've got a util::get_step_direction() in line.rs. It works by subtracting p1 - p0, getting the positive/negative sign of the QRT, and making a basic guess about direction.

I want a better one.

Using p0, p1, and origin (0, 0, 0), I can do some trig to determine what the direction of p0>p1 is properly. Remember to convert to PixelPoint before doing the trig.

Maybe do something to decide whether to call the direction "up" or "east." If it's really, really far up and one hex east, then that p0>p1 direction should be called up. Once you reach a place where they're about equal, then you can alternate between east and up. I guess favor east just because you have to favor one or the other.

This function should probably be like Direction::from((&Point, &Point)).

Implement rotate around provided point

Right now, rotate calculates the point rotated around 0,0,0. That's... actually not really useful. I should be able to provide a point to rotate around.

Would threading improve performance?

I don't know about threads really, but these functions are sometimes doing things that could be broken down into chunks, where chunk A doesn't really care about what happens in chunk B because the only thing that matters is that the return value is A + B.

Fix pixel point

Ah... I hastily pushed that. Right when I went to use it in line(), I realized... The pixel math from red blob is for 2d hexagons, so I can't just add my T to the height.

T would be represented by one pixel in height. That's the vertical side. The horizontal sides would be skewed for the pseudo-3D presentation. In the math, those sides are the same length as the height, but in pixels... it's complicated.

I don't actually need to calculate pseudo-3d pixel position right now. In fact, I might not need to at all. I could just draw the hexes on the screen and listen for mouse click events. Whatever tool I use would probably tell me, "Hey. He clicked on this hex sprite. This one. It has these axial coordinates." So I don't think I'd have to convert pixel to hex in most cases.

So... remove that bad math from the From<Point> bit in PixelPoint.

Make a CubePoint instead of Point::s()

CubePoint can have generic coordinate types so he can also replace FloatPoint::s(). Then I'll do something like this:

let CubePoint(q, r, s, t) = my_point.into();

Instead of this:

let FloatPoint(q, r, t) = my_float;
let s = my_float.s();

impl Add for HasValues

I'm currently doing something like this for #7:

pub fn distance_2d<T: HasValues>(point: &T, other: &T) -> i32 {
  let diff: Point = &point.to_point() - &other.to_point();
  let (q, r, s) = diff.values_cube_2d();
  let distance = (q.abs() + r.abs() + s.abs()) / 2;

  distance
}

I'd like to do let diff: Point = &point - &other; or let (q, r, t) = &point - &other; Maybe the second one is better since it doesn't require a conversion to Point unless I'm actually looking for that... in which case I'll use the new From::from or Point::from_values stuff.

Anyway, this HasValues - HasValues bit wasn't working. If I had it, then I could also get rid of the Add/Sub implementation on Point.

Figure out point iterations

I wrote a HasValues#into_vec(). I'm not so sure that's the proper solution here.

For example, the work I'm doing for ray() has a step where I need to do A + B * C where A: i32, B: f32, C: f32 for each element of three structs (A, A, A, A), (B, B, B, B), (C, C, C, C). So A0 + B0 * C0, A1 + B1 * C1, etc.

I can't use HasValues#into_vec() here even if I wanted to. This isn't a HasValues. It's a tuple of f32.

Here's some primitive work:

pub fn merge<T, U, V, W>(
  a: (T, T, T, T),
  b: (U, U, U, U),
  op: V,
) -> (W, W, W, W) where V: Fn(T, U) -> W {
  (op(a.0, b.0), op(a.1, b.1), op(a.2, b.2), op(a.3, b.3))
}

pub fn map<T, U, V>(
  a: (T, T, T, T),
  op: U,
) -> (V, V, V, V) where U: Fn(T) -> V {
  (op(a.0), op(a.1), op(a.2), op(a.3))
}

pub fn main() {

  let a = (1, 1, 1, 1);
  let b = (2.5f32, 1f32, 2.5f32, 2.5f32);
  let op = |x, y| x as f32 == y;
  let result = merge(a, b, op);

  println!("{:?}", result);

  println!("{:?}", map(a, |x| x as f32 + 1.5f32));

}

I don't like that either. I'd have to write a lot of functions, and I'd have to duplicate the work for tuples of three, tuples of four... Just no.

So maybe a HasValues should be different. It could be a struct which is backed by a private vector. I don't want someone adding or removing or even altering values in this vector. If I give someone access to it, I would just provide an iterator and let them do map(), collect(), etc.

This HasValues would need to be HasValues<T>. It would impl From<(T, T, T, T)>.

Maybe it wouldn't even be the HasValues trait but maybe more of a struct. It could still implement HasValues. So... maybe this work would be done on the Point struct. With the ability to provide a function for mapping, I could iterate over a Point<f32>, convert from f32 to i32, and collect back into a Point<i32>.

Idk though. I kind of appreciate the idea of having a Point.q property, but at the same time I've been using point.values() and stuff like that. I'm not even using that Point.q property.

Make tests run faster

The compiled tests (not in a /// doc ) are way faster, but I want the docs to have those examples. Can I maybe use an annotation to put a compiled test into the doc? Maybe even from a separate test file?

Make flood() aware of walls

#39 will be used to make ray() aware of walls, but that's only necessary because we don't know the direction we're moving in. With Flood(), we do. It's terribly inefficient to run trig for each zig zag in a flood when we already know where we're going. It's going in each direction in a for loop, so I just have to check for walls blocking movement in that direction.

Improve on HasValues, generics, etc.

A Prism contains a Point. If I ever need to get a Point from a Prism, it's easy. What is the value of the HasValues? It lets me say that I'll take a <T: HasValues> instead of a <T: Into<Point>>? That's no real benefit. It lets me calculate the s coordinate? No. That should be a function on the Point which the Prism wraps. Seems overcomplicated for no value.

And that my_point.values().into() approach to copying the Point into a new Point? What? Ok. I didn't want to derive Copy on Point because it felt wrong to use Copy... but sometimes I'm copying those primitives, and values().into() isn't good. It could be &my_point.into(), which copies the primitives.

Steps along line should only be manhattan distance 1

See this test:

use std::collections::HashSet;

use hex_math::{line, Point};

let point: Point = Point::new(1, 2, 5);
let other: Point = Point::new(3, 4, 10);
let set: HashSet<Point> = line(&point, &other);

assert!(set.contains(&Point::new(1, 2, 5)));
assert!(set.contains(&Point::new(2, 2, 6)));
assert!(set.contains(&Point::new(2, 3, 8)));
assert!(set.contains(&Point::new(3, 3, 9)));
assert!(set.contains(&Point::new(3, 4, 10)));
assert_eq!(set.len(), 5);

Wrong. The manhattan distance from (1, 2, 5) to (2, 2, 6) is 2. There should not be a step with a distance of 2.

This bug causes other problems as well. I wrote the code to make ray aware of walls (#41), and I think it might need different tests if this bug didn't exist. The wall finding code is based on the idea that each step is manhattan distance 1 away from the previous step.

Are there more things to implement from RedBlobGames?

flood_with_strength, ray_with_strength

fn ray_with_strength(point, i32 range, map<point, f32 strength>);

flood with strength

The idea is that the ray/flood would be able to pass through certain semi-opaque points. However, passing through a point with strength 0.5 would reduce the range by an extra 0.5.

Could be useful to have these. For example:

  • A player runs. A goblin is technically in range, but there are walls between them. Can the goblin hear the running?
  • A mage shoots a fireball. The player is hiding behind a barrel. Does the fireball hit the player? How much is the blast reduced by the barrel? What if the barrel was a stone wall?

Accept generic point, prism, etc in functions

Some fn foo(point: Point) could instead be fn foo<T>(point: T) where Point: From<T>. Then instead of directly using point, I would first let point: Point = From::from(point);. After that, I'll have a proper Point to use for distance() or whatever.

I just added hex_math::Prism, but there would maybe be others... especially when I eventually need to start keeping track of things like which player is on which hex.

I would then be able to directly use structs like Prism in my functions.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.