Skip to content

Instantly share code, notes, and snippets.

@laktek
Created July 14, 2009 19:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save laktek/147140 to your computer and use it in GitHub Desktop.
Save laktek/147140 to your computer and use it in GitHub Desktop.
module Message
class AbstractMessage
attr_accessor :body, :api_version, :api_format, :message_type, :source_id, :destination_id, :checksum
def initialize
@api_version = 1
@api_format = 1
@message_type = 1
@source_id = 64071
@destination_id = 64072
@checksum = 1
@body ||= ""
end
def serialize
output = ""
output << [@api_version].pack('c')
output << [@api_format].pack('c')
output << [@message_type].pack('c')
output << [].pack('x')
output << [@source_id].pack('n')
output << [@destination_id].pack('n')
output << [body_length].pack('n')
output << [].pack('x2')
output << [].pack('x3')
output << [@checksum].pack('c')
output << @body
end
def deserialize(message)
@api_version = message[0]
@api_format = message[1]
@message_type = message[2]
@source_id = message[4..5].unpack('n')[0]
@destination_id = message[6..7].unpack('n')[0]
@message_length = message[8..9].unpack('n')[0]
@checksum = message[15]
@body = message[16..(@message_length + 15)]
self
end
def prepare_message_body
end
private
def body_length
(@body && @body.length) || 0
end
end
class InitiationMessage < AbstractMessage
def initialize(options={})
@game_id = options.fetch(:game_id){ "000" }
prepare_message_body
super()
end
def prepare_message_body
@body = "[GID=#{@game_id}]"
end
end
class RejectMessage < AbstractMessage
def initialize(options={})
@game_id = options.fetch(:game_id){ "000" }
prepare_message_body
super()
end
def prepare_message_body
@body = "[GID=#{@game_id},RC=01]"
end
end
class FireSalvoMessage < AbstractMessage
def initialize(options={})
@game_id = options.fetch(:game_id){ "000" }
@salvo_id = options.fetch(:salvo_id){ "1" }
@targets = options.fetch(:targets){ [] }
prepare_message_body
super()
end
def prepare_message_body
@body = "[GID=#{@game_id},SID=#{@salvo_id},NOT=#{@targets.length},#{print_targets(@targets)}]"
end
private
def print_targets(values)
list_of_targets = []
values.each_with_index do |value, index|
list_of_targets << "T#{index + 1}=#{value}"
end
list_of_targets.join(",")
end
end
class SalvoResponseMessage < AbstractMessage
def initialize(options={})
@game_id = options.fetch(:game_id){ "000" }
@salvo_id = options.fetch(:salvo_id){ "1" }
@sunk_all = options.fetch(:sunk_all){ false } ? 1 : 0
@hits = options.fetch(:hits){ 0 }
@ships_sunk = options.fetch(:ships_sunk){ [] }
prepare_message_body
super()
end
def prepare_message_body
@body = "[GID=#{@game_id},SID=#{@salvo_id},SAS=#{@sunk_all},NOH=#{@hits},NSS=#{@ships_sunk.length},#{print_ships_sunk(@ships_sunk)}]"
end
private
def print_ships_sunk(values)
output = []
values.each_with_index do |value, index|
output << "SS#{index + 1}=#{value}"
end
output.join(",")
end
end
end
require "message"
describe "Abstract Message" do
before do
@msg = Message::AbstractMessage.new
@msg.body = "Test Body"
@it = @msg.serialize
end
it "should be at least 16 bytes" do
@it.length.should >= 16
end
it "should hold the api version in the first byte" do
@it[0].should == @msg.api_version
end
it "should hold the api format in the second byte" do
@it[1].should == @msg.api_format
end
it "should hold the message type in the third byte" do
@it[2].should == @msg.message_type
end
it "should hold the source id in the 5th & 6th bytes" do
@it[4..5].unpack('n').first.should == @msg.source_id
end
it "should hold the destination id in the 7th & 8th bytes" do
@it[6..7].unpack('n').first.should == @msg.destination_id
end
it "should hold the message body length on the 9th & 10th bytes" do
@it[8..9].unpack('n').first.should == @msg.body.length
end
it "should contain the given body" do
@it[16, @it[8..9].unpack('n').first].should == @msg.body
end
end
describe "Initiation Message" do
before do
@msg = Message::InitiationMessage.new(:game_id => "001")
end
it "should contain only the unique game ID" do
@msg.body.should == "[GID=001]"
end
end
describe "Rejection Message" do
before do
@msg = Message::RejectMessage.new(:game_id => "001")
end
it "should contain the unique game ID" do
@msg.body.should match(/GID=001/)
end
it "should contain the rejection code" do
@msg.body.should match(/RC=01/)
end
end
describe "Fire Salvo Message" do
before do
@msg = Message::FireSalvoMessage.new(:game_id => "001", :salvo_id => 1,
:targets => ["A1B1", "A2B2", "A3B3"])
end
it "should contain the unique game ID" do
@msg.body.should match(/GID=001/)
end
it "should contain the salvo ID" do
@msg.body.should match(/SID=1/)
end
it "should contain the number of targets" do
@msg.body.should match(/NOT=3/)
end
it "should contain the three salvo targets" do
@msg.body.should match(/T1=A1B1,T2=A2B2,T3=A3B3/)
end
end
describe "Salvo Response Message" do
before do
@msg = Message::SalvoResponseMessage.new(:game_id => "001", :salvo_id => 1, :sunk_all => false, :hits => 2,
:ships_sunk => [1, 2])
end
it "should contain the unique game ID" do
@msg.body.should match(/GID=001/)
end
it "should contain the salvo ID" do
@msg.body.should match(/SID=1/)
end
it "should indicate whether all ships were sunk" do
@msg.body.should match(/SAS=0/)
end
it "should indicate how many targets were hit" do
@msg.body.should match(/NOH=2/)
end
it "should indicate how many ships were sunk" do
@msg.body.should match(/NSS=2/)
end
it "should give the types of the ships sunk" do
#there is a problem of the format. should start from 0 or 1, also do need []?
@msg.body.should match(/SS1=1,SS2=2/)
end
end
Abstract Message
- should be at least 16 bytes
- should hold the api version in the first byte
- should hold the api format in the second byte
- should hold the message type in the third byte
- should hold the source id in the 5th & 6th bytes
- should hold the destination id in the 7th & 8th bytes
- should hold the message body length on the 9th & 10th bytes
- should contain the given body
Initiation Message
- should contain only the unique game ID
Rejection Message
- should contain the unique game ID
- should contain the rejection code
Fire Salvo Message
- should contain the unique game ID
- should contain the salvo ID
- should contain the number of targets
- should contain the three salvo targets
Salvo Response Message
- should contain the unique game ID
- should contain the salvo ID
- should indicate whether all ships were sunk
- should indicate how many targets were hit
- should indicate how many ships were sunk
- should give the types of the ships sunk
Finished in 0.007944 seconds
21 examples, 0 failures
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment