Advertisement
Guest User

Untitled

a guest
Jul 7th, 2015
264
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 16.78 KB | None | 0 0
  1. module Robot
  2. class CLI
  3.  
  4. def initialize
  5. options = {:file => nil , :x => 5 , :y => 5}
  6. parser = OptionParser.new do|opts|
  7. opts.banner = "Usage: toyrobot [options]"
  8.  
  9. opts.on('-f', '--file filepath', 'Filepath for input commands') do |filename|
  10. options[:file] = filename
  11. end
  12.  
  13. opts.on('-x', '--xcoordinate X', 'Max X co-ordinate(Number)') do |max_x|
  14. begin
  15. options[:x] = Integer(max_x)
  16. rescue
  17. puts "Invalid x argument"
  18. puts opts
  19. exit
  20. end
  21. end
  22.  
  23. opts.on('-y', '--ycoordinate Y', 'Max Y co-ordinate(Number)') do |max_y|
  24. begin
  25. options[:y] = Integer(max_y)
  26. rescue
  27. puts "Invalid y argument"
  28. puts opts
  29. exit
  30. end
  31. end
  32.  
  33. opts.on('-h', '--help', 'Displays Help') do
  34. puts opts
  35. exit
  36. end
  37. end
  38. parser.parse!
  39. @application = Robot::App.new(options)
  40. end
  41.  
  42. def start
  43. @application.start
  44. end
  45.  
  46. end
  47. end
  48.  
  49.  
  50. module Robot
  51. class App
  52. def initialize(opts)
  53. @input_file = opts[:file].nil? ? STDIN : File.open(opts[:file])
  54. @simulator = Robot::Simulator.new opts[:x], opts[:y]
  55. end
  56.  
  57. def start
  58. command = read_command
  59. while (command) do
  60. $logger.debug("Received command #{command}")
  61. begin
  62. @simulator.execute command
  63. rescue => e
  64. $logger.error(e)
  65. end
  66. command = read_command
  67. end
  68. end
  69.  
  70. def read_command
  71. print "# " if @input_file == STDIN
  72. command = @input_file.gets
  73. exit if command.nil? || (command.chomp.downcase == ".quit")
  74. command
  75. end
  76.  
  77. end
  78. end
  79.  
  80. module Robot
  81. class CommandParser
  82.  
  83. @@number_reg = /^d+$/
  84.  
  85. # allowed_commnads is a hash
  86. # with value as :method => [[arg types],[regex_range_for_strings_only]]
  87. def initialize(allowed_commands)
  88. @allowed_commands = allowed_commands
  89. $logger.info("Allowed commands are #{@allowed_commands.keys}")
  90. end
  91.  
  92. def parse command
  93. $logger.debug("Parsing command #{command}")
  94. args = command.split " "
  95. method = args.delete_at(0)
  96.  
  97. if valid?(method, args)
  98. update_args! method , args
  99. yield method , args
  100. return true
  101. else
  102. $logger.warn("Parsing failed. Invalid #{command}")
  103. return false
  104. end
  105. end
  106.  
  107.  
  108. private
  109.  
  110. def update_args! method , args
  111. @allowed_commands[method][0].each_with_index do |arg_type,i|
  112. case arg_type
  113. when :number
  114. args[i] = args[i].to_i
  115. when :string
  116. end
  117. end
  118. end
  119.  
  120.  
  121. def valid? (method , args)
  122. return false unless @allowed_commands.has_key? method
  123.  
  124. unless @allowed_commands[method].nil?
  125. return false unless @allowed_commands[method][0].size == args.size
  126. @allowed_commands[method][0].each_with_index do |arg_type,i|
  127. case arg_type
  128. when :number
  129. return false unless args[i] =~ @@number_reg
  130. when :string
  131. allowed_reg = @allowed_commands[method][1][i]
  132. unless allowed_reg.nil?
  133. return false unless args[i] =~ /#{allowed_reg}/
  134. end
  135. end
  136. end
  137. end
  138. return true
  139. end
  140.  
  141. end
  142. end
  143.  
  144. module Robot
  145.  
  146. class Direction
  147.  
  148. attr_accessor :direction
  149.  
  150. def initialize(direction)
  151. @direction = direction
  152. end
  153.  
  154. @NORTH = Direction.new "NORTH"
  155. @SOUTH = Direction.new "SOUTH"
  156. @EAST = Direction.new "EAST"
  157. @WEST = Direction.new "WEST"
  158.  
  159. @CLOCKWISE_DIRECTIONS = [@NORTH,@EAST,@SOUTH,@WEST]
  160.  
  161. def to_s
  162. @direction
  163. end
  164.  
  165. class << self
  166. attr_accessor :NORTH, :SOUTH, :EAST, :WEST
  167. end
  168.  
  169. def self.find(direction)
  170. @CLOCKWISE_DIRECTIONS.find{|d| d.direction == direction.upcase }
  171. end
  172.  
  173. def self.left(direction)
  174. @CLOCKWISE_DIRECTIONS[( @CLOCKWISE_DIRECTIONS.index(direction) - 1 ) % 4]
  175. end
  176.  
  177. def self.right(direction)
  178. @CLOCKWISE_DIRECTIONS[( @CLOCKWISE_DIRECTIONS.index(direction) + 1 ) % 4]
  179. end
  180.  
  181. end
  182.  
  183. end
  184.  
  185.  
  186. module Robot
  187. class Position
  188.  
  189. attr_accessor :x, :y, :direction
  190.  
  191. def initialize(x,y,direction)
  192. @x = x
  193. @y = y
  194. @direction = direction
  195. end
  196.  
  197. def to_s
  198. "X= #{@x} Y=#{@y} Facing=#{@direction.to_s}"
  199. end
  200.  
  201. def equal?(another_position)
  202. self.x == another_position.x &&
  203. self.y == another_position.y &&
  204. self.direction == another_position.direction
  205. end
  206.  
  207. def move
  208. curr_postion = self.dup
  209. case curr_postion.direction
  210. when Direction.NORTH
  211. curr_postion.y +=1
  212. when Direction.SOUTH
  213. curr_postion.y -=1
  214. when Direction.EAST
  215. curr_postion.x +=1
  216. when Direction.WEST
  217. curr_postion.x -=1
  218. end
  219. curr_postion
  220. end
  221.  
  222. def left
  223. curr_postion = self.dup
  224. curr_postion.direction = Direction.left @direction
  225. curr_postion
  226. end
  227.  
  228. def right
  229. curr_postion = self.dup
  230. curr_postion.direction = Direction.right @direction
  231. curr_postion
  232. end
  233.  
  234. end
  235. end
  236.  
  237. module Robot
  238.  
  239. class Simulator
  240.  
  241. attr_accessor :toy_robot
  242.  
  243. def initialize max_x, max_y
  244. commands = {
  245. "PLACE" => [
  246. [:number , :number , :string],
  247. [nil,nil,"^NORTH$|^SOUTH$|^EAST$|^WEST$"]
  248. ],
  249. "MOVE" => [[],[]],
  250. "LEFT" => [[],[]],
  251. "RIGHT" => [[],[]],
  252. "REPORT" => [[],[]]
  253. }
  254. @command_parser = CommandParser.new(commands)
  255. @table = Table.new max_x , max_y
  256. @toy_robot = ToyRobot.new
  257. $logger.info "Simulator created successfully."
  258. end
  259.  
  260. def execute(command)
  261. r = @command_parser.parse(command) do |method,args|
  262. $logger.debug("#{method.downcase} - args #{args}")
  263. self.send( method.downcase , * args)
  264. end
  265. $logger.debug(@toy_robot.to_s)
  266. end
  267.  
  268. def place x , y , face
  269. if @table.inside?(x, y)
  270. @toy_robot.position = Position.new(x, y, Direction.find(face))
  271. @toy_robot.placed = true
  272. end
  273. end
  274.  
  275. def move
  276. return unless @toy_robot.placed
  277. next_position = @toy_robot.position.move
  278. if @table.inside? next_position.x , next_position.y
  279. @toy_robot.position = next_position
  280. else
  281. ignore
  282. end
  283. end
  284.  
  285. def left
  286. return unless @toy_robot.placed
  287. @toy_robot.position = @toy_robot.position.left
  288. end
  289.  
  290. def right
  291. return unless @toy_robot.placed
  292. @toy_robot.position = @toy_robot.position.right
  293. end
  294.  
  295. def report
  296. if @toy_robot.placed
  297. puts "#{@toy_robot.position.x} #{@toy_robot.position.y} #{@toy_robot.position.direction}"
  298. else
  299. puts "Robot is not placed yet. Please use PLACE command to place the robot."
  300. end
  301. end
  302.  
  303. def ignore
  304. $logger.debug "Ignored step towards #{toy_robot.position.direction}"
  305. end
  306.  
  307. end
  308. end
  309.  
  310. module Robot
  311. class Table
  312.  
  313. def initialize max_x , max_y
  314. @MAX_X = max_x
  315. @MAX_Y = max_y
  316. $logger.info "Table boundaries are #{@MAX_X},#{@MAX_Y}"
  317. end
  318.  
  319. def inside? x,y
  320. return ((0..@MAX_X-1) === x) && ((0..@MAX_Y-1) === y)
  321. end
  322.  
  323. end
  324. end
  325.  
  326.  
  327. module Robot
  328. class ToyRobot
  329. attr_accessor :position, :placed
  330.  
  331. def initialize
  332. @position = nil
  333. @placed = false
  334. $logger.info "Toy Robot created successfully."
  335. end
  336.  
  337. def to_s
  338. if @placed
  339. "Placed at #{@position.to_s}"
  340. else
  341. "Not placed"
  342. end
  343. end
  344.  
  345. end
  346. end
  347.  
  348.  
  349. $LOAD_PATH << File.join(File.dirname(__FILE__))
  350. require "optparse"
  351. require "logger"
  352. require "robot/version"
  353. require 'robot/command_parser'
  354. require 'robot/table'
  355. require 'robot/position'
  356. require 'robot/toy_robot'
  357. require "robot/direction"
  358. require "robot/simulator"
  359. require "robot/app"
  360. require "robot/cli"
  361.  
  362. $logger = Logger.new('log/toy_robot.log')
  363.  
  364. require_relative '../spec_helper'
  365. require_relative '../../lib/robot'
  366.  
  367. include Robot
  368.  
  369. describe CommandParser do
  370.  
  371. subject(:command_parser) {CommandParser.new({
  372. "Salute" => [
  373. [:string],
  374. ["^Hello$|^Namaste$"]
  375. ],
  376.  
  377. "Name" => [
  378. [:string],
  379. []
  380. ] ,
  381. "Age" => [
  382. [:number],
  383. []
  384. ]
  385.  
  386. })}
  387.  
  388.  
  389. context "#with valid command" do
  390.  
  391. context "with range" do
  392. before {
  393. @ran = false
  394. @called = command_parser.parse "Salute Hello" do |m,a|
  395. @ran = true
  396. @m = m
  397. @a = a
  398. end
  399. }
  400.  
  401. it { expect(@ran).to be true }
  402. it { expect(@called).to be true }
  403. it { expect(@a.size).to be 1 }
  404. it { expect(@a[0]).to match "Hello" }
  405. it { expect(@m).to match "Salute" }
  406. end
  407.  
  408.  
  409. context "without range" do
  410.  
  411. context "with string" do
  412. before {
  413. @ran = false
  414. @called = command_parser.parse "Name Tom" do |m,a|
  415. @ran = true
  416. @m = m
  417. @a = a
  418. end
  419. }
  420.  
  421. it { expect(@ran).to be true }
  422. it { expect(@called).to be true }
  423. it { expect(@a.size).to be 1 }
  424. it { expect(@a[0]).to match "Tom" }
  425. it { expect(@m).to match "Name" }
  426. end
  427.  
  428. context "with number" do
  429. before {
  430. @ran = false
  431. @called = command_parser.parse "Age 50" do |m,a|
  432. @ran = true
  433. @m = m
  434. @a = a
  435. end
  436. }
  437.  
  438. it { expect(@ran).to be true }
  439. it { expect(@called).to be true }
  440. it { expect(@a.size).to be 1 }
  441. it { expect(@a[0]).to be 50 }
  442. it { expect(@m).to match "Age"}
  443. end
  444. end
  445. end
  446.  
  447.  
  448.  
  449. context "#with invalid command" do
  450. before {
  451. @ran = false
  452. @called = command_parser.parse "Salute Hi" do |m,a|
  453. @ran = true
  454. end
  455. }
  456.  
  457. it { expect(@called).to be false }
  458. it { expect(@called).to be false }
  459. end
  460.  
  461. end
  462.  
  463. require_relative '../spec_helper'
  464. require_relative '../../lib/robot'
  465. include Robot
  466.  
  467. describe Direction do
  468.  
  469. describe ":: finds directions" do
  470. it { expect( Direction.find "NORTH" ).to be Direction.NORTH }
  471. it { expect( Direction.find "SOUTH" ).to be Direction.SOUTH }
  472. it { expect( Direction.find "EAST" ).to be Direction.EAST }
  473. it { expect( Direction.find "WEST" ).to be Direction.WEST }
  474. end
  475.  
  476. describe ":: turns left" do
  477. it { expect( Direction.left Direction.NORTH ).to be Direction.WEST }
  478. it { expect( Direction.left Direction.SOUTH ).to be Direction.EAST }
  479. it { expect( Direction.left Direction.EAST ).to be Direction.NORTH }
  480. it { expect( Direction.left Direction.WEST ).to be Direction.SOUTH }
  481. end
  482.  
  483. describe ":: turns right" do
  484. it { expect( Direction.right Direction.NORTH ).to be Direction.EAST }
  485. it { expect( Direction.right Direction.SOUTH ).to be Direction.WEST }
  486. it { expect( Direction.right Direction.EAST ).to be Direction.SOUTH }
  487. it { expect( Direction.right Direction.WEST ).to be Direction.NORTH }
  488. end
  489.  
  490. end
  491.  
  492.  
  493. require_relative '../spec_helper'
  494. require_relative '../../lib/robot'
  495. include Robot
  496.  
  497. describe Position do
  498.  
  499. context "#moves correctly to north" do
  500. before {
  501. @position = Position.new(2, 3, Direction.NORTH)
  502. @curr_position = @position.move
  503. }
  504.  
  505. it { expect(@curr_position.x).to be 2 }
  506. it { expect(@curr_position.y).to be 4 }
  507. it { expect(@curr_position.direction).to be Direction.NORTH }
  508.  
  509. end
  510.  
  511. context "#moves correctly to south" do
  512. before {
  513. @position = Position.new(2, 3, Direction.SOUTH)
  514. @curr_position = @position.move
  515. }
  516.  
  517. it { expect(@curr_position.x).to be 2 }
  518. it { expect(@curr_position.y).to be 2 }
  519. it { expect(@curr_position.direction).to be Direction.SOUTH }
  520.  
  521. end
  522.  
  523. context "#moves correctly to east" do
  524. before {
  525. @position = Position.new(2, 3, Direction.EAST)
  526. @curr_position = @position.move
  527. }
  528.  
  529. it { expect(@curr_position.x).to be 3 }
  530. it { expect(@curr_position.y).to be 3 }
  531. it { expect(@curr_position.direction).to be Direction.EAST }
  532.  
  533. end
  534.  
  535. context "#moves correctly to west" do
  536. before {
  537. @position = Position.new(2, 3, Direction.WEST)
  538. @curr_position = @position.move
  539. }
  540.  
  541. it { expect(@curr_position.x).to be 1 }
  542. it { expect(@curr_position.y).to be 3 }
  543. it { expect(@curr_position.direction).to be Direction.WEST }
  544.  
  545. end
  546.  
  547. context "#turns left" do
  548. before {
  549. start_position = Position.new(2, 3, Direction.WEST)
  550. @position = start_position.left
  551. }
  552.  
  553. it { expect(@position.x).to be 2 }
  554. it { expect(@position.y).to be 3 }
  555. it { expect(@position.direction).to be Direction.SOUTH }
  556. end
  557.  
  558. context "#turns right" do
  559. before {
  560. start_position = Position.new(2, 3, Direction.WEST)
  561. @position = start_position.right
  562. }
  563.  
  564. it { expect(@position.x).to be 2 }
  565. it { expect(@position.y).to be 3 }
  566. it { expect(@position.direction).to be Direction.NORTH }
  567. end
  568.  
  569. end
  570.  
  571. require_relative '../spec_helper'
  572. require_relative '../../lib/robot'
  573. include Robot
  574.  
  575. describe Simulator do
  576.  
  577. describe "#gets placed" do
  578. before {
  579. @simulator = Simulator.new 5,5
  580. @simulator.place 2, 3, "NORTH"
  581. }
  582.  
  583. it { expect(@simulator.toy_robot.position).to be Position.new(2,3,Direction.NORTH) }
  584.  
  585. end
  586.  
  587. describe "#moves" do
  588.  
  589. context "when inside table" do
  590. before {
  591. @simulator = Simulator.new 5,5
  592. @simulator.place 2, 3, "NORTH"
  593. @simulator.move
  594. }
  595. it { expect(@simulator.toy_robot.position).to be Position.new(2,4,Direction.NORTH) }
  596. end
  597.  
  598. context "when at edge of table" do
  599. before {
  600. @simulator = Simulator.new 5,5
  601. @simulator.place 4, 4, "NORTH"
  602. @simulator.move
  603. }
  604. it { expect(@simulator.toy_robot.position).to be Position.new(4,4,Direction.NORTH) }
  605. end
  606.  
  607. end
  608.  
  609. describe "# turns" do
  610.  
  611. context "when faced north" do
  612. before {
  613. @simulator = Simulator.new 5,5
  614. @simulator.place 2, 2, "NORTH"
  615. }
  616.  
  617. it { @simulator.left ; expect(@simulator.toy_robot.position).to be Position.new(2,2,Direction.WEST) }
  618. it { @simulator.right ; expect(@simulator.toy_robot.position).to be Position.new(2,2,Direction.EAST) }
  619. end
  620.  
  621. context "when faced south" do
  622. before {
  623. @simulator = Simulator.new 5,5
  624. @simulator.place 2, 2, "SOUTH"
  625. }
  626.  
  627. it { @simulator.left ; expect(@simulator.toy_robot.position).to be Position.new(2,2,Direction.EAST) }
  628. it { @simulator.right ; expect(@simulator.toy_robot.position).to be Position.new(2,2,Direction.WEST) }
  629. end
  630.  
  631. context "when faced east" do
  632. before {
  633. @simulator = Simulator.new 5,5
  634. @simulator.place 2, 2, "EAST"
  635. }
  636.  
  637. it { @simulator.left ; expect(@simulator.toy_robot.position).to be Position.new(2,2,Direction.NORTH) }
  638. it { @simulator.right ; expect(@simulator.toy_robot.position).to be Position.new(2,2,Direction.SOUTH) }
  639. end
  640.  
  641. context "when faced west" do
  642. before {
  643. @simulator = Simulator.new 5,5
  644. @simulator.place 2, 2, "WEST"
  645. }
  646.  
  647. it { @simulator.left ; expect(@simulator.toy_robot.position).to be Position.new(2,2,Direction.SOUTH) }
  648. it { @simulator.right ; expect(@simulator.toy_robot.position).to be Position.new(2,2,Direction.NORTH) }
  649. end
  650.  
  651. end
  652.  
  653. end
  654.  
  655. require_relative '../spec_helper'
  656. require_relative '../../lib/robot'
  657. include Robot
  658.  
  659. describe Table do
  660.  
  661. context "#checks boundry" do
  662. before { @t = Table.new 5,5 }
  663. it {expect(@t.inside?(2,3)).to be true}
  664. it {expect(@t.inside?(0,0)).to be true}
  665. it {expect(@t.inside?(5,5)).to be false}
  666. it {expect(@t.inside?(5,3)).to be false}
  667. it {expect(@t.inside?(3,5)).to be false}
  668. it {expect(@t.inside?(6,3)).to be false}
  669. it {expect(@t.inside?(3,6)).to be false}
  670. it {expect(@t.inside?(-1,0)).to be false}
  671. it {expect(@t.inside?(0,-1)).to be false}
  672. end
  673. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement