-- Hex: q,r,s. invariant that q+r+s=0 -- add, subtract -- constructor takes 3 positions and rounds to closest hex centre. -- round to nearest int, rounding .5 away from 0. local function round(x) if x<0 then return math.ceil(x-0.5) else return math.floor(x+0.5) end end local Pos, Hex local SR3 = math.sqrt(3) Hex={} Hex.__index = Hex function Hex.make(q,r,s) s=s or -q-r assert(q+r+s==0,"hex coord doesn't meet invariant") return setmetatable({q=q,r=r,s=s},Hex) end function Hex.round(self) -- return a new Hex rounded to integer coordinates local fq,fr,fs = self.q,self.r,self.s -- round all to nearest integer -- find which was changed the most, reset that one to be coherent with the other two. local abs = math.abs local rq,rr,rs = round(fq),round(fr),round(fs) local dq,dr,ds = abs(fq-rq), abs(fr-rr), abs(fs-rs) if dq>dr and dq>ds then rq = -rr-rs elseif dr>ds then rr = -rq-rs else rs = -rq-rr end return Hex.make(rq,rr,rs) end function Hex.to_pos(self) local x = self.q*SR3 + self.r*(SR3/2) local y = self.r*(3/2) return Pos.make(x,y) end function Hex.__add(self,other) return Hex.make(self.q+other.q, self.r+other.r, self.s+other.s) end function Hex.__sub(self,other) return Hex.make(self.q-other.q, self.r-other.r, self.s-other.s) end function Hex.__tostring(self) return string.format("H(%.2f,%.2f)",self.q,self.r) end Pos = {} Pos.__index=Pos function Pos.make(x,y) return setmetatable({x=x,y=y},Pos) end function Pos.__add(self,other) return Pos.make(self.x+other.x,self.y+other.y) end function Pos.__sub(self,other) return Pos.make(self.x-other.x,self.y-other.y) end function Pos.__mul(a,b) if type(a) == "number" then return Pos.make(a*b.x,a*b.y) elseif type(b) == "number" then return Pos.make(a.x*b,a.y*b) else error("can only multiply Pos by scalar") end end function Pos.__div(a,b) assert(type(b) == "number","can only divide Pos by scalar, and can't divide scalar by Pos") return a*(1/b) end function Pos.lensq(self) return self.x^2 + self.y^2 end function Pos.len(self) return math.sqrt(self:lensq()) end function Pos.norm(self) return self/self:len() end function Pos.dot(self,other) return self.x*other.x + self.y*other.y end function Pos.to_hex(self) local q = self.x*(SR3/3) - self.y*(1/3) local r = (2/3)*self.y return Hex.make(q,r,-q-r) end function Pos.__tostring(self) return string.format("(%.2f,%.2f)",self.x,self.y) end return {Hex=Hex,Pos=Pos}