Commits

Tomoya Chiba  committed 5b12284

コードの全面書き直し、持ち時間機能の実装

  • Participants
  • Parent commits 8992442

Comments (0)

Files changed (11)

File client/reversi-client.coffee

   socket = io.connect socketURL
   revClient = null
 
+  doYourTurn = (color) ->
+    html = "<p>Your Turn: #{if color == ReversiRule.black then 'black' else 'white'}</p>"
+    $(html).hide().prependTo('#chatlog').slideDown()
+
   socket.on 'notice login', (msg) ->
     html = "<p>login(room: #{msg.roomname}): #{msg.username}</p>"
     $(html).hide().prependTo('#chatlog').slideDown()
     html = "<p>logout(room: #{msg.roomname}): #{msg.username}</p>"
     $(html).hide().prependTo('#chatlog').slideDown()
 
-  socket.on 'game standby', ->
+  socket.on 'game standby', (data) ->
     revInterface = new ReversiInterface "#reversi-space", "reversi-board"
     revClient = new ReversiClient(revInterface, socket)
     revClient.mouseEventOn()
     revClient = null
 
   socket.on 'game turn', (res) ->
-    html = "<p>Your Turn: #{if res.color == ReversiRule.black then 'black' else 'white'}</p>"
-    $(html).hide().prependTo('#chatlog').slideDown()
+    doYourTurn(res.color)
 
   socket.on 'roomlist', (res) ->
     for idx, val of res

File controllers/reversi-room.coffee

+ReversiBoard = require('./reversi')
+machina = require('machina')()
+machina_extensions = require('../lib/machina_extensions')
 
-machina = require('machina')
+STARTTIME = 60000
+TIME_DEADLINE = -200
 
 ReversiRoom = machina.Fsm.extend
-  constructor: (@name) ->
+  initialize: (@name) ->
     @board = null
     @_players = []
     @_watchers = []
     @colors = [ReversiBoard.black, ReversiBoard.white]
+    @latestTime = undefined
+    @leftTime = {}
+
   initialState: 'empty'
   states:
     empty:
       login: (player) ->
         @_addUser(player)
 
-      beginWatch: (player) ->
+      watchIn: (player) ->
         @_addWatcher(player)
       
-      goWaiting: 'waiting' 
+      watchOut: (player) ->
+        @_removeWatcher(player)
+
+      transitionCheck: ->
+        if @_players.length >= 2
+          @handle('goFull')
+        else if @_players.length + @_watchers.length == 0
+          @handle('goEmpty')
+        else
+          @handle('goWaiting')
+      
+      goWaiting: 'waiting'
+      goFull: 'full'
 
     waiting:
       login: (player) ->
       logout: (player) ->
         @_removeUser(player)
 
-      beginWatch: (player) ->
+      watchIn: (player) ->
         @_addWatcher(player)
 
+      watchOut: (player) ->
+        @_removeWatcher(player)
+      
+      transitionCheck: ->
+        if @_players.length >= 2
+          @handle('goFull')
+        else if @_players.length + @_watchers.length == 0
+          @handle('goEmpty')
+        else
+          @handle('goWaiting')
+      
+      goEmpty: 'empty'
+      goFull: 'full'
+
+    full:
+      _onEnter: ->
+        @transition('game')
+
+      startGame: ->
+        @_doStartGame()
+
+      logout: (player) ->
+        @_removeUser(player)
+
+      watchIn: (player) ->
+        @_addWatcher(player)
+
+      watchOut: (player) ->
+        @_removeWatcher(player)
+      
+      transitionCheck: ->
+        if @_players.length >= 2
+          @handle('goFull')
+        else if @_players.length + @_watchers.length == 0
+          @handle('goEmpty')
+        else
+          @handle('goWaiting')
+      
+      goWaiting: 'waiting'
+      goEmpty: 'empty'
+
     game:
       _onEnter: ->
+
         @_suffleColor()
         blackplayer = @findPlayerByColor(ReversiBoard.black)
         whiteplayer = @findPlayerByColor(ReversiBoard.white)
-        autoPassFlag = 
-          black: @options[blackplayer].options.autoPass
-          white: @options[whiteplayer].options.autoPass
+        autoPassFlag =
+          black: blackplayer.options.autoPass
+          white: whiteplayer.options.autoPass
+
+        @leftTime = {}
+        @leftTime[blackplayer.name] = STARTTIME
+        @leftTime[whiteplayer.name] = STARTTIME
 
         @board = new ReversiBoard(autoPassFlag)
-        @emit 'gameStart', 
-          turnPlayer: findPlayerByColor(ReversiBoard.black)
+        @emit 'gameStart',
+          colors:
+            black: blackplayer
+            white: whiteplayer
+          time: STARTTIME
+
+      _onExit: ->
+        @_stone = @board.countStone()
+
+      cancelGame: ->
+        @handle 'endGame', 'GAME_CANCELED',
+          black: 'TIE'
+          white: 'TIE'
+
+      illegalEndGame: (illigalPlayer, reason) ->
+        blackplayer = @findPlayerByColor(ReversiBoard.black)
+        whiteplayer = @findPlayerByColor(ReversiBoard.white)
+        @handle 'endGame', reason,
+          black: if blackplayer.name == illigalPlayer.name then 'LOSE' else 'WIN'
+          white: if whiteplayer.name == illigalPlayer.name then 'LOSE' else 'WIN'
+       
+      illegalCheck: (player, error) ->
+        @handle('illegalEndGame', player, 'ILLIGAL_MOVE') if player.options.illigalMoveLose
+        throw error
+
+      timeCheck: (player, time) ->
+        diff = @timeDiff(time)
+        console.log "timeBefore: #{@latestTime}"
+        console.log "timeAfter: #{time}"
+        console.log "timeDiff: #{diff}"
+
+        lTime = (@leftTime[player.name] || STARTTIME) - diff
+        if lTime < TIME_DEADLINE
+          @handle 'illegalEndGame', player, "TIME_UP"
+          return
+        else if lTime < 0
+          lTime = 0
+
+        @leftTime[player.name] = lTime
+        @emit 'ack', 
+          player: player
+          time: lTime
+
+      endGame: (@_reason, @_wlSpecial) ->
+        @transition('gameEnd')
 
       logout: (player) ->
         @_removeUser(player)
       
+      watchIn: (player) ->
+        @_addWatcher(player)
+
+      watchOut: (player) ->
+        @_removeWatcher(player)
+      
       move: (player, x, y) ->
-        @_parseMoveResult @board.put(x, y, @getColor(player))
+        update = null
+        autoPassCount = 0
+
+        @board.once 'update', (res) ->
+          update = res
+        @board.on 'autoPass', ->
+          autoPassCount++
+
+        try
+          @board.move(x, y, @getColor(player))
+          @board.removeAllListeners()
+          @_parseMoveResult player,
+            update: update
+            autoPassCount: autoPassCount
+        catch error
+          @board.removeAllListeners()
+          @handle 'illegalCheck', player, error
+          
 
       pass: (player) ->
-        @_parseMoveResult @board.pass(@getColor(player))
+        autoPassCount = 0
+
+        @board.on 'autoPass', ->
+          autoPassCount++
+
+        try
+          @board.pass(@getColor(player))
+          @board.removeAllListeners()
+          @_parseMoveResult player,
+            autoPassCount: autoPassCount
+        catch error
+          @board.removeAllListeners()
+          @handle 'illegalCheck', player, error
+
+      transitionCheck: ->
+        if @_players.length <= 2
+          @handle('cancelGame')
+        else if @_players.length + @_watchers.length == 0
+          @handle('goEmpty')
+      
+      emitTurn: ->
+        nextColor = @turnColor()
+        nextTurnPlayer = @turnPlayer()
+        @emit 'nextTurn',
+          turnPlayer: nextTurnPlayer
+          color: nextColor
 
-      _onExit: ->
-        @_stone = @board.countStone()
-        @_result = @handle('gameResult')
+      emitAllUpdates: (player) ->
+        @emit 'allUpdates',
+          toSend: player
+          updates: @board.updateStack.list
+
+      turnColor: ->
+        @board.turn
+
+      turnPlayer: ->
+        @findPlayerByColor(@turnColor())
 
     gameEnd:
       _onEnter: ->
+        @handle('gameResult')
         @emit 'gameEnd', @_result
 
       logout: (player) ->
       gameResult: ->
         self = @
 
-        result = {}
-        @players.forEach (player) ->
-          result[player.username] = self.handle('playerResult', player)
-        result
+        playerResult = []
+        @_players.forEach (player) ->
+          playerResult.push
+            player: player
+            result: self.get('playerResult', player)
+            
+        @_result =
+          forPlayer: playerResult
+          forWatcher:
+            watchers: @_watchers
+            result:
+              reason: @_reason
+              black: @_stone.black
+              white: @_stone.white
 
       playerResult: (player) ->
-        userColor = @getColor(username)
+        userColor = @getColor(player)
 
         issue = null
         reason = null
-        if @_illigalPlayer
-          issue = if @_illigalPlayer == username then 'LOSE' else 'WIN'
+        wl = new Array(2)
+
+        if @_wlSpecial
+          wl = [@_wlSpecial.white, @_wlSpecial.black]
         else
-          wl = new Array(2)
           if @_stone.white > @_stone.black
             wl = ['WIN', 'LOSE']
           else if @_stone.white < @_stone.black
           else
             wl = ['TIE', 'TIE']
 
-          idx = [ReversiBoard.white, ReversiBoard.black].indexOf(userColor)
-          issue = wl[idx]
-
+        idx = [ReversiBoard.white, ReversiBoard.black].indexOf(userColor)
+        issue = wl[idx]
+      
         color: userColor
-        issue: issue 
+        issue: issue
         reason: @_reason
         black: @_stone.black
         white: @_stone.white
+        
+      transitionCheck: ->
+        if @_players.length + @_watchers.length == 0
+          @handle('goEmpty')
+        else
+          @handle('goWaiting')
 
-  _parseMoveResult: (result) ->
-    nextColor = @board.turn
-    nextTurnPlayer = @turnPlayer()
-
-    unless result.success
-      @_endGame('ILLEGAL_MOVE', player) if player.options.illigalMoveLose
-      throw 'illigalMove'
+      goWaiting: 'waiting'
+      goEmpty: 'empty'
 
-    if result.autoPass > 0
-      @emit 'autoPass', result.autoPass
+  _parseMoveResult: (player, result) ->
+    if result.update
+      @emit 'move',
+        update: result.update
+        player: player
+    else
+      @emit 'pass',
+        player: player
 
-    @_endGame('DOUBLE_PASS') if @board.isGameEnd()
+    if result.autoPassCount > 0
+      @emit 'autoPass', result.autoPassCount
 
-    if @state == 'game'
-      @emit 'nextTurn',
-        nextTurnPlayer: nextTurnPlayer
-        nextColor: nextColor
+    @handle('endGame', 'DOUBLE_PASS') if @board.isGameEnd()
+    @handle('emitTurn')
 
     return @
   
-  _endGame: (reason, illigalPlayer) ->
-    @_illigalPlayer = illigalPlayer
-    @_reason = reason
-    @transition('gameEnd')
-
   _suffleColor: ->
     if Math.random() > 0.5
       tmp = @colors[0]
       @colors[0] = @colors[1]
       @colors[1] = tmp
 
+  _transitionCheck: ->
+    if @_players.length >= 2
+      @handle('goFull')
+    else if @_players.length + @_watchers.length == 0
+      @handle('goEmpty')
+    else
+      @handle('goWaiting')
+
+  _nameValidation: (name) ->
+    for arr in [@_players, @_watchers]
+      for p in arr
+        if p.name == name
+          throw new Error('alreadyExistName')
+
+
   _addUser: (player) ->
-    @_players.push username
-    @handle('goWaiting')
-    @transition('game') if @_players.length >= 2
+    @_nameValidation()
+    @_players.push player
+
+    @emit 'login', player
+    @handle 'transitionCheck'
     @
     
   _removeUser: (player) ->
     idx = @findIdxByName(@_players, player.name)
     if idx >= 0
       @_players.splice(idx, 1)
-      @transition('waiting') if @_players.length < 2
+
+      @emit 'logout', player
+      @handle 'transitionCheck'
       @
     else
-      throw 'playerNotFound'
+      throw new Error('playerNotFound')
 
   _addWatcher: (player) ->
-    @_watchers.push username
+    @_nameValidation()
+    @_watchers.push player
     @handle('goWaiting')
+
+    @emit 'watchIn', player
+    @handle 'emitAllUpdates', player
+    @handle 'transitionCheck'
     @
     
   _removeWatcher: (player) ->
     idx = findIdxByName(@_watchers, player.name)
     if idx >= 0
       @_watchers.splice(idx, 1)
-      @_emptyCheck()
+
+      @emit 'watchOut', player
+      @handle 'transitionCheck'
       @
     else
-      throw 'playerNotFound'
+      throw new Error('playerNotFound')
     
-  _emptyCheck: ->
-    if @_players.length + @_watchers.length == 0
-      @transition('empty')
-      
   getColor: (player) ->
-    idx = @players.indexOf(player.username)
+    idx = @findIdxByName(@_players, player.name)
     return null if idx < 0 || idx > 1
     @colors[idx]
 
-  findIdxByName: (array, username) ->
+  findIdxByName: (array, name) ->
     for i, e of array
-      return i if e.username == username
+      return i if e.name == name
     return -1
 
   findPlayerByColor: (color) ->
 
   login: (player) ->
     @handle('login', player)
+    @
 
   logout: (player) ->
     @handle('logout', player)
+    @
+
+  startGame: ->
+    @handle('startGame')
+
+  watchIn: (player) ->
+    @handle('watchIn', player)
+    @
+
+  watchOut: (player) ->
+    @handle('watchOut', player)
+    @
+
+  move: (player, x, y) ->
+    @handle('move', player, x, y)
+    @
 
-  beginWatch: (player) ->
-    @handle('beginWatch', player)
+  pass: (player) ->
+    @handle('pass', player)
+    @
 
-  endWatch: (player) ->
-    @handle('endWatch', player)
+  isEmpty: ->
+    @state == 'empty'
 
-ReversiRoom.login = (room, client) ->
-  unless room then room = new ReversiRoom()
-  room.login(client)
+  get: ->
+    machina_extensions.get.apply @, arguments
 
-  room
+  turnColor: ->
+    @get('turnColor')
 
-ReversiRoom.logout = (room, client) ->
-  room.logout(client)
-  if room.state == 'empty'
-    room = null
+  turnPlayer: ->
+    @get('turnPlayer')
 
-  room
+  saveTime: ->
+    @latestTime = new Date().getTime()
 
-ReversiRoom.beginWatch = (room, client) ->
-  unless room then room = new ReversiRoom()
-  room.beginWatch(client)
+  timeDiff: (time) ->
+    time - @latestTime
 
-  room
-    
-ReversiRoom.endWatch = (room, client) ->
-  room.endWatch(client)
-  if room.state == 'empty'
-    room = null
+  timeCheck: (player, time) ->
+    @handle('timeCheck', player, time)
 
-  room
+  players: -> @_players
+  watchers: -> @_watchers
 
-    
+module.exports = ReversiRoom
 
 

File controllers/reversi-server.coffee

 ReversiBoard = require('./reversi')
-machina = require('machina')
-
-class ReversiRoom
-  constructor:  ->
-    @state = 'waiting'
-    @board = null
-    @players = []
-    @options = {}
-    @illigalPlayer = null
-    @illigalReason = null
-    @colors = [ReversiBoard.black, ReversiBoard.white]
-
-  _addUser: (username, options) ->
-    if @players.length < 2
-      if @players.indexOf(username) < 0
-        @players.push username
-        @options[username] = options
-        return true
-    false
-
-  _removeUser: (username) ->
-    if (idx = @players.indexOf(username)) >= 0
-      @players.splice(idx, 1)
-      delete @options[username]
-      true
-    else
-      false
-
-  turnPlayer: () ->
-    return null unless @state == 'game'
-    idx = @colors.indexOf(@board.turn)
-    if idx > -1 
-      @players[idx]
-    else
-      null
-
-  isGameEnd: ->
-    @board.isGameEnd()
-
-  illigalMoveGameEnd: (username, reason) ->
-    @illigalPlayer = username
-    @illigalReason = reason
-    @board.gameEnd()
-
-  gameResult: ->
-    self = @
-
-    result = {}
-    @players.forEach (username) ->
-      result[username] = self.playerResult(username)
-    result
-
-  playerResult: (username) ->
-    userColor = @getColor(username)
-    stone = @board.countStone()
-
-    issue = null
-    reason = null
-    if @illigalPlayer
-      issue = if @illigalPlayer == username then 'LOSE' else 'WIN'
-      reason = @illigalReason
-    else
-      wl = new Array(2)
-      if stone.white > stone.black
-        wl = ['WIN', 'LOSE']
-      else if stone.white < stone.black
-        wl = ['LOSE', 'WIN']
-      else
-        wl = ['TIE', 'TIE']
-
-      idx = [ReversiBoard.white, ReversiBoard.black].indexOf(userColor)
-      issue = wl[idx]
-      reason = "DOUBLE_PASS"
-
-    result =
-      color: userColor
-      issue: issue 
-      reason: reason
-      black: stone.black
-      white: stone.white
-  
-  countStone: ->
-    @board.countStone()
-
-  startGame: () ->
-    return false unless @state == 'waiting' && @players.length == 2
-    @illigalPlayer = null
-    @state = 'game'
-    if Math.random() > 0.5
-      tmp = @colors[0]
-      @colors[0] = @colors[1]
-      @colors[1] = tmp
-
-    blackplayer = @findUserByColor(ReversiBoard.black)
-    whiteplayer = @findUserByColor(ReversiBoard.white)
-
-    # ap = @options[blackplayer].autoPass || @options[whiteplayer].autoPass
-    autoPassFlag = 
-      black: @options[blackplayer].autoPass
-      white: @options[whiteplayer].autoPass
-
-    @board = new ReversiBoard(autoPassFlag)
-
-    true
-
-  cancelGame: () ->
-    @state = 'waiting'
-
-  getColor: (username) ->
-    idx = @players.indexOf(username)
-    return null if idx < 0 || idx > 1
-    @colors[idx]
-
-  findUserByColor: (color) ->
-    idx = @colors.indexOf(color)
-    return null if idx < 0 || idx > 1
-    @players[idx]
-
-  move: (username, x, y) ->
-    return null unless @state == 'game'
-    result = @board.put(x, y, @getColor(username))
-
-    update = if result then result.update else null
-    autoPass = if result then result.autoPass else 0
-    success = update != null
-    gameEnd = @isGameEnd()
-    @state = 'waiting' if gameEnd
-
-    if !success && @options[username].illigalMoveLose
-      @illigalMoveGameEnd(username,"ILLEGAL_MOVE")
-
-    result =
-      success: success 
-      update: update
-      autoPass: autoPass
-      gameEnd: gameEnd
-      nextTurnPlayer: @turnPlayer()
-      nextColor: @board.turn
-
-  pass: (username) ->
-    return {success: false} unless @turnPlayer() == username
-    result = @board.pass()
-    gameEnd = @isGameEnd()
-    @state = 'waiting' if gameEnd
-
-    if !result.success && @options[username].illigalMoveLose
-      @illigalMoveGameEnd(username, "ILLEGAL_MOVE")
-
-    result =
-      success: result.success
-      autoPass: result.autoPass
-      gameEnd: gameEnd
-      nextTurnPlayer: @turnPlayer()
-      nextColor: @board.turn
-
-
-  _gameStartInfo: ->
-    players: @players
-    nextTurnPlayer: @turnPlayer()
-    nextColor: @board.turn
-
-  @login: (room, username, options, callback) ->
-    if !room then room = new ReversiRoom
-    success = room._addUser(username, options)
-    isGameStart = room.startGame()
-    status =
-      success: success 
-      gameStart: isGameStart
-      gameInfo: room._gameStartInfo() if isGameStart
-
-    callback(room, status) if callback
-
-  @logout: (room, username, callback) ->
-    success = false
-    cancelflag = false
-    if room
-      success = room._removeUser(username)
-      cancelflag = true if room.state == 'game'
-      room = undefined if room.players.length == 0
-
-    status = 
-      success: success
-      gameCancel: success && cancelflag
-
-    room.cancelGame() if room && status.gameCancel 
-    if callback then callback(room, status)
-
-  @states: ['waiting', 'game']
+ReversiRoom = require('./reversi-room')
+machina = require('machina')()
+machina_extensions = require('../lib/machina_extensions')
 
 class ReversiServer
   constructor: () ->
     @clearMem()
 
   clearMem: ->
-    @_roomList = {}
-    @_userInfo = {}
+    @_rooms = {}
+    @_users = {}
     @_connectors = []
 
-  userInfo: (username) ->
-    @_userInfo[username]
-  
-  roomInfo: (roomname) ->
-    @_roomList[roomname]
- 
-  connectors: ->
-    @_connectors
-
-  registerConnector: (connector) ->
-    @_connectors.push connector
-
-  register: (username, client, connector, options) ->
-    @_userInfo[username] =
-      state: {type: 'waiting'}
-      client: client
-      connector: connector
-      options: options || {}
-
   login: (username, roomname) ->
-    self = @
+    player = @_users[username]
+    room = @_rooms[roomname]
+    unless room
+      room = new ReversiRoom(roomname)
+      @_rooms[roomname] = room
+      @_onRoomEvent room
 
-    info = @_userInfo[username]
-    return @fail(username, 'login failed') if info && info.state.type == 'login'
-    maskedName = @maskName(username)
-
-    ReversiRoom.login @_roomList[roomname], username, info.options, (room, status) ->
-      self._roomList[roomname] = room
-
-      if status.success
-        self._userInfo[username].state = 
-          type: 'login'
-          roomname: roomname
-        self.requestJoinGroup(username, roomname)
-
-        self.requestNoticeAll 'login',
-          username: maskedName
-          roomname: roomname
-
-        if status.gameStart
-          self.requestNoticeToGroup roomname, 'game standby',
-            roomname: roomname
-            players: status.gameInfo.players
-            nextTurnPlayer: status.gameInfo.nextTurnPlayer
-            nextColor: status.gameInfo.nextColor
-        console.log "done/login room: #{roomname}, id: #{username}"
-      else
-        self.fail(username, 'login failed')
-        console.log "fault/login room: #{roomname}, id: #{username}"
+    try
+      player.login room
+      player.joinGroup roomname
+    catch error
+      console.log 'loginFailed'
+      console.log error
+      player.notice 'loginFailed',
+        reason: error
+    finally
+      room.saveTime() if room
 
   logout: (username) ->
-    self = @
-
-    info = @_userInfo[username]
-    unless info && info.state.type == 'login'
-      return self.fail(username, 'logout failed') 
-    maskedName = @maskName(username)
+    player = @_users[username]
+    room = player.room()
 
-    roomname = info.state.roomname
-    ReversiRoom.logout @_roomList[roomname], username, (room, status) ->
-      self._roomList[roomname] = room
-      delete self._roomList[roomname] unless room
+    try
+      player.logout()
+      room.emit 'sendEvents'
+      player.leaveGroup room.name
+      if room.isEmpty()
+        @_offRoomEvent room
+        delete @_rooms[room.name]
 
-      if status.success
-        self._userInfo[username].state =
-          type: 'waiting'
+    catch error
+      console.log 'logoutFailed'
+      console.log error
+      player.notice 'logoutFailed',
+        reason: error
+        #throw error
+    finally
+      room.saveTime() if room
 
-        self.requestNoticeAll 'logout',
-          username: maskedName
-          roomname: roomname
+  disconnect: (username) ->
+    @logout(username)
+    @_remove(username)
 
-        if status.gameCancel
-          self.requestNoticeToGroup roomname, 'game cancel',
-            roomname: roomname
+  move: (username, x, y) ->
+    player = @_users[username]
+    room = player.room()
 
-        self.requestLeaveGroup(username, roomname)
-        console.log "done/logout room: #{roomname}, id: #{username}"
+    try
+      player.move x, y
+    catch error
+      console.log 'moveFailed'
+      console.log error
+      player.notice 'failed move',
+        reason: error
+        #throw error
+    finally
+      player.notice 'move submitted'
+      if room
+        room.emit 'sendEvents' 
+        room.saveTime()
 
-      else
-        self.fail(username, 'logout failed') 
-        console.log "fault/logout room: #{roomname}, id: #{username}"
+  pass: (username) ->
+    player = @_users[username]
+    room = player.room()
 
-  disconnect: (username) -> @logout(username)
+    try
+      player.pass()
+    catch error
+      console.log 'passFailed'
+      console.log error
+      player.notice 'failed pass',
+        reason: error
+    finally
+      player.notice 'pass submitted'
+      if room
+        room.emit 'sendEvents' 
+        room.saveTime()
 
-  move: (username, x, y) ->
-    info = @_userInfo[username]
-    return @moveResponseNotice(username, false) unless info
-    switch info.state.type 
-      when 'login'
-        roomname = info.state.roomname
-        room = @_roomList[roomname]
-        
-        result = if x && y then room.move(username, x, y) else room.pass(username)
-
-        @moveResponseNotice(username, result.success)
-        if result.success
-          if result.update
-            @requestNoticeToGroup roomname, 'game update',
-              update: result.update
-              username: username
-              isLastTurn: result.gameEnd
-          else
-            @requestNoticeToGroup roomname, 'game pass',
-              username: username
-              isLastTurn: result.gameEnd
-
-          if result.autoPass > 0
-            @requestNoticeToGroup roomname, 'game autopass',
-              username: username 
-              isLastTurn: result.gameEnd
-
-          if result.gameEnd
-            @noticeGameEnd(roomname)
-          else
-            @requestNotice result.nextTurnPlayer, 'game turn',
-              color: result.nextColor
-      else
-        @moveResponseNotice(username, false)
- 
-  pass: (username) ->
-    @move(username, null, null)
+  timeCheck: (username) ->
+    time = new Date().getTime()
+    player = @_users[username]
+    player.timeCheck(time)
 
-  moveResponseNotice: (username, success) ->
-    @requestNotice username, 'move submitted',
-      success: success
+  registerConnector: (connector) ->
+    @_connectors.push connector
 
-  fail: (username, msg) ->
-    @requestNotice username, msg
+  register: (username, client, connector, options) ->
+    if @_users[username]
+      connector.notice client, 'registerFailed'
+      # throw new Error('alreadyExistName')
+    @_users[username] = new Player(username, client, connector, options)
 
-  noticeGameEnd: (roomname) ->
-    room = @_roomList[roomname]
-    results = room.gameResult()
-    for username, result of results
-      @requestNotice username, 'game end', 
-        result
+  _remove: (username) ->
+    delete @_users[username]
 
   requestNoticeAll: (type, data) ->
     @_connectors.forEach (connector) ->
     @_connectors.forEach (connector) ->
       connector.noticeToGroup(groupname, type, data)
 
-  requestNotice: (username, type, data) ->
-    cinfo = @findConnectInfo(username)
-    cinfo.connector.notice(cinfo.client, type, data)
-
-  requestJoinGroup: (username, groupname) ->
-    cinfo = @findConnectInfo(username)
-    cinfo.connector.joinGroup(username, cinfo.client, groupname)
-
-  requestLeaveGroup: (username, groupname) ->
-    cinfo = @findConnectInfo(username)
-    cinfo.connector.leaveGroup(username, cinfo.client, groupname)
-
-  findConnectInfo: (username) ->
-    client: @_userInfo[username].client
-    connector: @_userInfo[username].connector
-
   noticeRoomlist: (username) ->
     roomlist = @genRoomListForMsg()
-    @requestNotice username, 'roomlist',
+    player = @_users[username]
+
+    player.notice 'roomlist',
       roomlist: roomlist
 
   genRoomListForMsg: () ->
-    for idx, val of @_roomList
+    for idx, val of @_rooms
       if val
-        name: idx
-        players: val.players
+        players = for p in val.players()
+          p.name
+        watchers = for p in val.watchers()
+          p.name
 
-  maskName: (username) ->
-    info = @_userInfo[username]
-    if info.options && info.options.maskName
-      ReversiServer.genMaskName(username)
-    else
-      username
-
-  @genMaskName: (str) ->
-    if str.length > 5 then str.slice(0, 5) + "*****" else str
+        name: idx
+        players: players
+        watchers: watchers
 
-class ReversiServer
-  login: (username, roomname) ->
-    player = @_players[username]
-    room = @_rooms[roomname]
-    unless room
-      room = new ReversiRoom(roomname)
-      @_room[roomname] = room
-      @_setEvRecv room
+  _onRoomEvent: (room) ->
+    self = @
 
-    try
-      player.login room
+    room.on 'login', (player) ->
+      self.requestNoticeAll 'login',
+        roomname: room.name
+        username: player.name
 
-      player.notice 'login',
-        username: username
-        roomname: roomname
-    catch error
-      player.notice 'failed login',
-        reason: error
-  logout: (username) ->
-    player = @_players[username]
+    room.on 'logout', (player) ->
+      self.requestNoticeAll 'logout',
+        roomname: room.name
+        username: player.name
 
-    try
-      player.logout
+    room.on 'watchIn', (player) ->
+      self.requestNoticeAll 'watchIn',
+        roomname: room.name
+        username: player.name
 
-      player.notice 'logout',
-        username: username
-    catch error
-      player.notice 'failed logout',
-        reason: error 
-  move: (username, x, y) ->
-    player = @_players[username]
+    room.on 'watchOut', (player) ->
+      self.requestNoticeAll 'watchOut',
+        roomname: room.name
+        username: player.name
 
-  _setEvRecv: (room) ->
     room.on 'gameStart', (res) ->
-      turnPlayer = res.turnPlayer
+      players = for _, p of res.colors
+        p.name
+
+      for colorname, player of res.colors
+        player.notice 'gameStart',
+          color: if colorname == 'black' then ReversiBoard.black else ReversiBoard.white
+          username: player.name
+          players: players
+          time: res.time
 
     room.on 'gameEnd', (res) ->
+      for i in res.forPlayer
+        i.player.notice 'gameEnd', i.result
+
+      for i in res.forWatcher.watchers
+        i.notice 'WatchingGameEnd',
+          res.forWatcher.result
+
+    room.on 'move', (res) ->
+      self.requestNoticeToGroup room.name, 'move',
+        update: res.update
+        username: res.player.name
+
+    room.on 'pass', (res) ->
+      self.requestNoticeToGroup room.name, 'pass',
+        username: res.player.name
 
     room.on 'autoPass', (res) ->
-      autoPassCount = res
+      self.requestNoticeToGroup room.name, 'autoPass',
+        count: res
 
     room.on 'nextTurn', (res) ->
-      turnPlayer = res.turnPlayer
-      color = res.color
+      res.turnPlayer.notice 'nextTurn',
+        color: res.color
+
+    room.on 'ack', (res) ->
+      res.player.notice 'ack', 
+        time: res.time
+
+    room.on 'sendEvents', ->
+      self.requestNoticeToGroup room.name, 'sendEvents'
+
+  _offRoomEvent: (room) ->
+    room.off()
+
+machina_get = () ->
 
 Player = machina.Fsm.extend
-  constructor: (@username, @client, @connector, @options) ->
+  initialize: (@name, @client, @connector, @options) ->
     @options = @options || {}
 
   initialState: 'waiting'
   states:
-    waiting: 
+    waiting:
       login: (room) ->
-        result = ReversiRoom.login room, @
-        if result.success
-          @_room = result.room 
-          @transition('login')
-        result
-      beginWatch: (room) ->
-        result = ReversiRoom.beginWatch room, @
-        if result.success
-          @_room = result.room 
-          @transition('login')
-        result
+        @_room = @
+        @_room = room.login @
+        @transition('login')
+      watchIn: (room) ->
+        @_room = @
+        @_room = room.watchIn @
+        @transition('login')
 
     login:
       room: -> @_room
+      login: ->
+        throw new Error('Double login')
       logout: ->
-        result = ReversiRoom.logout @_room, @
-        if result.success
-          @_room = null
-          @transition('waiting')
-        result
+        room = @_room
+        room.logout @
+        @_room = null
+        @transition('waiting')
       move: (x, y) ->
-        @_room.move(@, x, y) 
+        @_room.move(@, x, y)
       pass: ->
         @_room.pass(@)
+      timeCheck: (time)->
+        @_room.timeCheck(@, time)
 
-    watching: 
+    watching:
       room: -> @_room
-      endWatch: ->
-        result = ReversiRoom.endWatch @_room, @
-        if result.success
-          @_room = null
-          @transition('waiting')
-        result
+      watchOut: ->
+        room = @_room
+        @_room = null
+        room.watchOut @
+        @transition('waiting')
 
   login: (room) ->
     @handle('login', room)
   logout: ->
     @handle('logout')
-  beginWatch: (room) ->
-    @handle('beginWatch', room)
-  endWatch: ->
-    @handle('endWatch')
+  watchIn: (room) ->
+    @handle('watchIn', room)
+  watchOut: ->
+    @handle('watchOut')
+  move: (x, y) ->
+    @handle('move', x, y)
+  pass: ->
+    @handle('pass')
+  timeCheck: (time) ->
+    @handle('timeCheck', time)
+
+  get: ->
+    machina_extensions.get.apply @, arguments
+
   room: ->
-    @handle('room')
+    @get('room')
+
+  notice: (type, data) ->
+    @connector.notice(@client, type, data)
+
+  joinGroup: (groupname) ->
+    @connector.joinGroup(@name, @client, groupname)
+
+  leaveGroup: (groupname) ->
+    @connector.leaveGroup(@name, @client, groupname)
+
 
 
 

File controllers/reversi.coffee

 
+{EventEmitter} = require 'events'
+
 class UpdateStack
-  constructor: (@point, @color, @next) ->
-    @revPoints = []
+
+  constructor:->
+    @list = []
+
+  push: (pt, color) ->
+    @list.push
+      point: pt
+      color: color
+      revPoints: []
 
   add: (pt) ->
-    @revPoints.push pt
+    @newest().revPoints.push pt
 
-  newest: () ->
-    point: @point
-    color: @color
-    revPoints: @revPoints
+  newest: ->
+    @list[@list.length - 1]
 
-class Reversi
+class Reversi extends EventEmitter
   @black = 1
   @white = -1
   @gameEnd = 0
     @board[5][4] = Reversi.black
 
     @turn = Reversi.black
-    @updateStack = null
+    @updateStack = new UpdateStack()
 
   colorXY: (x, y) -> @board[x][y]
 
-  canPutCheck: ->
+  canMoveCheck: ->
     for x in [1..8]
       for y in [1..8]
-        return true if @canPut(x, y, @turn)
+        return true if @canMove(x, y, @turn)
     false
   
-  put: (x, y, color) ->
-    return null unless @turn == color && @canPut(x, y, color)
+  move: (x, y, color) ->
+    throw new Error('illegalMove') unless @turn == color && @canMove(x, y, color)
     @passCount = 0
 
-    @updateStack = new UpdateStack(point(x, y), color, @updateStack)
+    @updateStack.push(point(x, y), color)
     @board[x][y] = color
     vector = surrounds(0, 0)
-    for pt, i in surrounds(x, y) 
+    for pt, i in surrounds(x, y)
       seqEnd = @_findSeqEnd.call(@, pt.x, pt.y, vector[i].x, vector[i].y, color)
       if seqEnd
         @_reverseSeq.call(@, pt.x, pt.y, vector[i].x, vector[i].y, seqEnd.x, seqEnd.y)
 
     count = 0
 
-    autoPassCount = @autoPass()
-    { update: @updateStack.newest(), autoPass: autoPassCount }
+    @emit 'update', @updateStack.newest()
+    @autoPass()
+    @
 
-  _pass: ->
-    unless @canPutCheck()
+  _doPass: ->
+    if @canMoveCheck()
+      throw new Error('illegalPass')
+    else
       if @passCount++ > 0
         @turn = Reversi.gameEnd
-        {success: true, autoPass: 0}
       else
         @turn = - @turn
-        autoPassCount = @autoPass()
-        {success: true, autoPass: autoPassCount}
-    else
-      {success: false, autoPass: 0}
+        @autoPass()
+
 
   pass: (color) ->
     if color == @turn
-      @pass()
+      @_doPass()
+      @
     else
-      return {success: false, autoPass: 0} 
+      throw new Error('illegalPass')
   
   autoPass: ->
-    colorKey = if @turn == Reversi.black then 'black' else 'white'
-    if @autoPassFlags[colorKey]
-      unless @turn == Reversi.gameEnd
-        result = @_pass()
-        return if result.success then result.autoPass + 1 else result.autoPass
-    return 0
-
-  canPut: (x, y, color) ->
+    try
+      colorKey = if @turn == Reversi.black then 'black' else 'white'
+      if @autoPassFlags[colorKey]
+        unless @turn == Reversi.gameEnd
+          @_doPass()
+          @emit 'autoPass'
+    catch _
+
+  canMove: (x, y, color) ->
     return false unless @colorXY(x, y) == 0
     vector = surrounds(0, 0)
     for pt, i in surrounds(x, y)

File controllers/socketio-connector.coffee

+Reversi = require('./reversi')
 
 # connector:
 #   need to implement:
           username: data.username
           roomname: data.roomname
 
-      when 'game turn'
+      when 'nextTurn'
         client.emit 'game turn',
           color: data.color
 
-      when 'game standby'
-        client.emit 'game standby',
-          name: data.name
-
-        client.socket(data.nextTurnPlayer).emit 'game turn',
-          color: data.nextColor
-
-      when 'game cancel'
-        client.emit 'game cancel',
-          name: data.name
-
-      when 'game update'
+      when 'gameStart'
+        client.emit 'game standby'
+
+        if data.color == Reversi.black
+          console.log client
+          client.emit 'game turn',
+            color: data.color
+
+      when 'gameEnd'
+        if data.reason == 'GAME_CANCELED'
+          client.emit 'game cancel'
+        else
+          client.emit 'game end',
+            data
+            # color: data.color
+            # issue: data.issue
+            # black: data.black
+            # white: data.white
+
+      when 'move'
         client.emit 'game update',
           data.update
           # point: data.point
           # color: data.color
           # revPoints: data.revPoints
           
-      when 'game end'
-        client.emit 'game end',
-          data
-          # color: data.color
-          # issue: data.issue
-          # black: data.black
-          # white: data.white
-
       when 'roomlist'
+        console.log data.roomlist
         client.emit 'roomlist',
           data.roomlist
 
       when 'move submitted'
-        client.emit 'move submitted',
-          success: data.success
+        client.emit 'move submitted'
 
 
 

File controllers/tcp-connector.coffee

       socket: socket
       buffer: ""
       username: null
+      eventStocks: {}
 
     console.log 'server -> tcp server created.'
 
     socket.on 'data', (data) ->
-      console.log "server-> #{data}/ from: #{socket.remoteAddress}:#{socket.remotePort}"
+      console.log "server<- #{data}/ from: #{socket.remoteAddress}:#{socket.remotePort}"
       parseCmd = TcpConnector.parser(client.buffer, data.toString())
       client.buffer = parseCmd.buffer
       if parseCmd.success
         self.doCommand(parseCmd, client)
 
     socket.on 'close', ->
-      console.log "server-> close connection #{socket.remoteAddress}:#{socket.remotePort}"
+      console.log "server -/- close connection #{socket.remoteAddress}:#{socket.remotePort}"
       self.operator.disconnect client.username if client.username
     
     socket.on 'error', ->
         @operator.register username, client, this,
           illigalMoveLose: true
           autoPass: false
-        @operator.login username, roomname 
+        @operator.login username, roomname
 
       when 'MOVE'
+        @operator.timeCheck(client.username)
         if com.args[0] == "PASS"
           @operator.pass client.username
         else
 
   joinGroup: (username, client, groupname) ->
     @groups[groupname] = {} unless @groups[groupname]
-    @groups[groupname][client.username] = client
+    @groups[groupname][username] = client
 
   leaveGroup: (username, client, groupname) ->
-    delete @groups[groupname][client.username]
+    if @groups[groupname][username]
+      g = @groups[groupname]
+      delete g[username]
 
   noticeAll: (type, data) ->
     for idx, val of @clients
       @notice(val, type, data) if val
 
   noticeToGroup: (groupname, type, data) ->
+    console.log @groups[groupname]
     for idx, val of @groups[groupname]
       @notice(val, type, data) if val
 
   notice: (client, type, data) ->
     username = client.username
+    eventStocks = client.eventStocks
 
     switch type
-      when 'game standby'
+      when 'gameStart'
         oppPlayer = data.players[1 - data.players.indexOf(username)]
-        if username == data.nextTurnPlayer
-          client.socket.write "START BLACK #{oppPlayer} 60000\n"
-        else
-          client.socket.write "START WHITE #{oppPlayer} 60000\n"
-      when 'game update'
-        unless username == data.username || data.isLastTurn
-          ptstr = TcpConnector.convertPos(data.update.point.x, data.update.point.y)
-          client.socket.write "MOVE #{ptstr}\n"
-      when 'game pass'
-        unless username == data.username || data.isLastTurn
-          client.socket.write "MOVE PASS\n"
-      when 'game autopass'
-        if username == data.username && !data.isLastTurn
-          client.socket.write "MOVE PASS\n"
-      when 'move submitted'
-        if data.success
-          client.socket.write "ACK 60000\n"
-      when 'game end'
         if data.color == Reversi.black
-          client.socket.write "END #{data.issue} #{data.black} #{data.white} #{data.reason}\n"
+          @socketWrite client, "START BLACK #{oppPlayer} #{data.time}\n"
         else
-          client.socket.write "END #{data.issue} #{data.white} #{data.black} #{data.reason}\n"
-        client.socket.write "BYE\n"
+          @socketWrite client, "START WHITE #{oppPlayer} #{data.time}\n"
+      when 'ack'
+        @socketWrite client, "ACK #{data.time}\n"
+      when 'move'
+        console.log "stock: MOVE #{username} #{data}"
+        eventStocks.move = data
+      when 'pass'
+        console.log "stock: PASS #{username} #{data}"
+        eventStocks.pass = data
+      when 'autoPass'
+        console.log "stock: AUTOPASS #{username} #{data}"
+        eventStocks.autoPass = data
+      when 'sendEvents'
+        emitMove = true
+        emitPass = true
+        emitAutoPass = true
+        if eventStocks.gameEnd
+          if eventStocks.autoPass
+            emitAutoPass = false
+          else
+            emitMove = false
+            emitPass = false
+        
+        if eventStocks.move && emitMove
+          sdata = eventStocks.move
+          unless username == sdata.username
+            ptstr = TcpConnector.convertPos(sdata.update.point.x, sdata.update.point.y)
+            @socketWrite client, "MOVE #{ptstr}\n"
+
+        if eventStocks.pass && emitPass
+          sdata = eventStocks.pass
+          unless username == sdata.username
+            @socketWrite client, "MOVE PASS\n"
+
+        if eventStocks.autoPass && emitAutoPass
+          sdata = eventStocks.autoPass
+          if username == sdata.username
+            @socketWrite client, "MOVE PASS\n"
+
+
+        if eventStocks.gameEnd
+          sdata = eventStocks.gameEnd
+          if sdata.color == Reversi.black
+            @socketWrite client, "END #{sdata.issue} #{sdata.black} #{sdata.white} #{sdata.reason}\n"
+          else
+            @socketWrite client, "END #{sdata.issue} #{sdata.white} #{sdata.black} #{sdata.reason}\n"
+          client.socket.write "BYE\n"
+
+        client.eventStocks = {}
+      when 'gameEnd'
+        eventStocks.gameEnd = data
+      when 'registerFailed'
+        @socketWrite client, "ERROR REGISTER_FAILED"
+
+  socketWrite: (client, msg) ->
+    console.log "server-> #{msg}/ to: #{client.socket.remoteAddress}:#{client.socket.remotePort}"
+    client.socket.write msg
         
   @parser: (buffer, str) ->
     constr = buffer + str

File lib/machina_extensions.coffee

+
+exports.get = (inputType) ->
+  GETTING = 'getting'
+  GOT = 'got'
+  NO_GETTER = 'getter'
+  NEXT_HANDLER = 'handler'
+
+  if !@inExitHndler
+    states = @states
+    current = @state
+    args = [].slice.call(arguments)
+    getter = undefined
+    action = undefined
+
+    @currentActionArgs = args
+    if states[current][inputType] || states[current]['*'] || @['*']
+      getterName = if states[current][inputType] then inputType else '*'
+      catchAll = getterName is '*'
+
+      if states[current][getterName]
+        getter = states[current][getterName]
+        action = "#{current}.#{getterName}"
+      else
+        getter = @['*']
+        action = '*'
+      if !@_currentAction
+        @_currentAction = action
+      @emit.call @, GETTING,
+        inputType: inputType
+        args: args.slice(1)
+
+      if typeof getter is 'function'
+        getter = getter.apply @,
+          if catchAll then args else args.slice(1)
+      @emit.call @, GOT,
+        inputType: inputType
+        args: args.slice(1)
+      @_priorAction = @_currentAction
+      @_currentAction = ''
+      @processQueue NEXT_HANDLER
+
+    else
+      @emit.call @, NO_GETTER,
+        inputType: inputType
+        args: args.slice(1)
+    @currentActionArgs = undefined
+    getter

File test/controllers/reversi-room-test.coffee

+ReversiRoom = require('../../controllers/reversi-room')
+Reversi = require('../../controllers/reversi')
+chai = require('chai')
+chai.should()
+
+describe 'ReversiRoom', ->
+
+  room = null
+
+  beforeEach ->
+    room = new ReversiRoom('testroom')
+
+  describe 'login', ->
+    it 'login (create room and response)', (done) ->
+      testname = 'testuser'
+      user =
+        name: testname
+
+      room.on 'login', (res) ->
+        res.name.should.eql testname
+        done()
+
+      room.login(user)
+
+  describe 'logout', ->
+    it 'login and logout', (done) ->
+      count = 1
+      testname = 'testuser'
+      user =
+        name: testname
+
+      check = ->
+        if count-- <= 0
+          done()
+
+      room.on 'login', (res) ->
+        res.name.should.eql testname
+        check()
+
+      room.on 'logout', (res) ->
+        res.name.should.eql testname
+        check()
+
+      room.login(user)
+      room.logout(user)
+
+  describe 'pass', ->
+    it 'pass', (done) ->
+      count = 1
+      testname1 = 'testuser1'
+      testname2 = 'testuser2'
+      user1 =
+        name: testname1
+        options: 
+          autoPass: true
+      user2 =
+        name: testname2
+        options: 
+          autoPass: true
+      turnPlayer = null
+
+      check = ->
+        if count-- <= 0
+          done()
+
+      room.on 'gameStart', (res) ->
+        console.log arguments
+        turnPlayer = res.colors.black
+        check()
+
+      room.on 'pass', (res) ->
+        console.log arguments
+        check()
+
+      room.login(user1)
+      room.login(user2)
+
+      room.board.board[4][4] = Reversi.black
+      room.board.board[5][5] = Reversi.black
+      room.pass(turnPlayer)
+

File test/controllers/reversi-server-test.coffee

     revServer = new ReversiServer()
     testConnector = new TestConnector(revServer)
   
-  it 'registerConnector', ->
-    revServer.connectors().length.should.eql(1)
+  it 'registerConnector', (done) ->
+    # revServer.connectors().length.should.eql(1)
+    testConnector.noticeAllfunc = (type, data) ->
+        type.should.eql 'test'
+        data.should.eql 'testdata'
+        done()
+
+    revServer.requestNoticeAll('test', 'testdata')
 
   it 'register', ->
     username = 'testuser'
-    testClient = 
+    testClient =
       id: "test"
 
+
     revServer.register(username, testClient, testConnector)
 
-    info = revServer.userInfo(username)
 
-    info.state.type.should.eql('waiting')
-    info.client.should.eql(testClient)
-    info.connector.should.eql(testConnector)
+
+    # info = revServer.userInfo(username)
+
+    # info.state.type.should.eql('waiting')
+    # info.client.should.eql(testClient)
+    # info.connector.should.eql(testConnector)
 
   describe 'login', ->
     it 'login (create room and response)', (done) ->
       username = 'testuser'
       roomname = 'testroom'
-      testClient = 
+      testClient =
         id: "test"
 
       testConnector.noticeAllfunc = (type, data) ->
       username = 'testuser'
       roomname = 'testroom'
       roomname2 = 'testroom2'
-      testClient = 
+      testClient =
         id: "test"
-      count = 0
+      count = 2
+
+      check = ->
+        if count-- <= 1
+          done()
 
       testConnector.noticeAllfunc = (type, data) ->
         type.should.eql 'login'
         data.username.should.eql username
         data.roomname.should.eql roomname
-        done() if count++ > 0
+        check()
 
       testConnector.noticefunc = (client, type, data) ->
         client.should.eql testClient
-        type.should.eql 'login failed'
-        done() if count++ > 0
+        type.should.eql 'loginFailed'
+        check()
 
       revServer.register(username, testClient, testConnector)
       revServer.login(username, roomname)
     it 'login and logout', (done) ->
       username = 'testuser'
       roomname = 'testroom'
-      testClient = 
+      testClient =
         id: "test"
-      count = 0
+      count = 2
+
+      check = ->
+        console.log count
+        if count-- <= 1
+          done()
 
       testConnector.noticeAllfunc = (type, data) ->
         switch type
           when 'login'
             data.username.should.eql username
             data.roomname.should.eql roomname
-            count++
+            check()
           when 'logout'
             data.username.should.eql username
             data.roomname.should.eql roomname
-            done() if count++ > 0
+            check()
+          when 'loginFailed', 'logoutFailed'
+            console.log 'fail'
+            1.should.eql 2
 
       revServer.register(username, testClient, testConnector)
       revServer.login(username, roomname)
-      revServer.logout(username, roomname)
+      revServer.logout(username)
+
     it 'cannot logout before login', (done) ->
       username = 'testuser'
       roomname = 'testroom'
-      testClient = 
+      testClient =
         id: "test"
       count = 0
 
       testConnector.noticefunc = (client, type, data) ->
-        type.should.eql 'logout failed'
-        done() 
+        type.should.eql 'logoutFailed'
+        done()
 
       revServer.register(username, testClient, testConnector)
       revServer.logout(username, roomname)
       usernames = ['testuser1', 'testuser2']
       roomname = 'testroom'
       testClients = [{id: "test1"}, {id: "test2"}] 
-      count = 0
+      count = 4
 
       check = ->
-        done() if count++ > 1
+        done() if count-- <= 1
 
       testConnector.noticeAllfunc = (type, data) ->
         console.log arguments
                 data.roomname.should.be.eql roomname
                 check()
 
-      testConnector.noticeToGroupfunc = (groupname, type, data) ->
+      testConnector.noticefunc = (client, type, data) ->
         console.log arguments
         switch type
-          when 'game standby'
-            data.roomname.should.eql roomname
-            data.nextColor.should.eql Reversi.black
+          when 'gameStart'
+            data.time.should.eql(60000)
             check()
 
+
+
       revServer.register(usernames[0], testClients[0], testConnector)
       revServer.register(usernames[1], testClients[1], testConnector)
       revServer.login(usernames[0], roomname)
       usernames = ['testuser1', 'testuser2']
       roomname = 'testroom'
       testClients = [{id: "test1"}, {id: "test2"}] 
-      count = 0
+      count = 3
 
       check = ->
-        room = revServer.roomInfo(roomname)
-        room.state.should.eql 'waiting'
-        done()
+        done() if count-- <= 1
 
-      testConnector.noticeToGroupfunc = (groupname, type, data) ->
+      testConnector.noticefunc = (client, type, data) ->
+        console.log arguments
         switch type
-          when 'game standby'
-            data.roomname.should.eql roomname
-            check() if count++ > 0
-          when 'game cancel'
-            data.roomname.should.eql roomname
-            check() if count++ > 0
+          when 'gameStart'
+            check()
+          when 'gameEnd'
+            data.reason.should.eql('GAME_CANCELED')
+            check()
 
       revServer.register(usernames[0], testClients[0], testConnector)
       revServer.register(usernames[1], testClients[1], testConnector)
     it 'move', (done) ->
       usernames = ['testuser1', 'testuser2']
       roomname = 'testroom'
-      testClients = [{id: "test1"}, {id: "test2"}] 
-      count = 0
+      testClients = [{id: "test1"}, {id: "test2"}]
+      nextTurnPlayer = null
+      count = 2
 
       check = ->
-        done() if count++ > 1
+        done() if count-- <= 1
 
       testConnector.noticeToGroupfunc = (groupname, type, data) ->
         groupname.should.eql(roomname)
+        console.log arguments
         switch type
-          when 'game standby'
-            data.roomname.should.eql roomname
-            data.nextColor.should.eql Reversi.black
-            revServer.move(data.nextTurnPlayer, 3, 4)
-            check()
-          when 'game update'
+          when 'move'
             data.update.point.x.should.eql(3)
             data.update.point.y.should.eql(4)
             data.update.color.should.eql(Reversi.black)
             check()
 
       testConnector.noticefunc = (client, type, data) ->
+        console.log arguments
         switch type
-          when 'game turn'
+          when 'nextTurn'
             data.color.should.eql Reversi.white
-          when 'move submitted'
-            data.success.should.eql true
             check()
+          when 'gameStart'
+            if data.color == Reversi.black
+              nextTurnPlayer = data.username
 
       revServer.register(usernames[0], testClients[0], testConnector)
       revServer.register(usernames[1], testClients[1], testConnector)
       revServer.login(usernames[0], roomname)
       revServer.login(usernames[1], roomname)
+      revServer.move(nextTurnPlayer, 3, 4)
 
     it 'gameEnd', (done) ->
       usernames = ['testuser1', 'testuser2']
       roomname = 'testroom'
-      testClients = [{id: "test1"}, {id: "test2"}] 
-      count = 0
+      testClients =
+        [{id: "test1", name: 'testuser1'},
+        {id: "test2", name: 'testuser2'}]
+      count = 3
 
-      turnPlayer = new Array(2)
+      nextTurnPlayerName = null
+      nonTurnPlayerName = null
 
       check =  ->
-        if count++ > 1
+        if count-- <= 1
           done()
           # room = revServer.roomInfo(roomname)
 
       testConnector.noticeToGroupfunc = (groupname, type, data) ->
         groupname.should.eql(roomname)
+        console.log arguments
         switch type
-          when 'game standby'
-            room = revServer.roomInfo(roomname)
-
-            data.nextColor.should.eql Reversi.black
-            turnPlayer[0] = if data.nextTurnPlayer == 'testuser1' then 0 else 1
-            turnPlayer[1] = 1 - turnPlayer[0]
-
-            room.board.board[5][5] = Reversi.black
-            revServer.move(data.nextTurnPlayer, 3, 4)
-          when 'game update'
+          when 'move'
             data.update.point.x.should.eql(3)
             data.update.point.y.should.eql(4)
             data.update.color.should.eql(Reversi.black)
             check()
 
       testConnector.noticefunc = (client, type, data) ->
+        console.log arguments
         switch type
-          when 'game turn'
+          when 'gameStart'
+
+            if data.color == Reversi.black
+              nextTurnPlayerName = data.username
+            else
+              nonTurnPlayerName = data.username
+
+          when 'nextTurn'
             data.color.should.eql Reversi.white
-          when 'game end'
-            switch client.id
-              when testClients[turnPlayer[0]].id
+          when 'gameEnd'
+            switch client.name
+              when nextTurnPlayerName
                 data.color.should.eql Reversi.black
                 data.issue.should.eql 'WIN'
-              when testClients[turnPlayer[1]].id
+              when nonTurnPlayerName
                 data.color.should.eql Reversi.white
                 data.issue.should.eql 'LOSE'
             data.black.should.eql(5)
       revServer.login(usernames[0], roomname)
       revServer.login(usernames[1], roomname)
 
+      revServer._rooms[roomname].board.board[5][5] = Reversi.black
+      revServer.move(nextTurnPlayerName, 3, 4)
+

File test/controllers/reversi-test.coffee

-assert = require('assert')
+assert = require('chai').assert
+expect = require('chai').expect
 ReversiBoard = require('../../controllers/reversi')
+chai = require('chai')
<