@ -2,7 +2,7 @@
 
		
	
		
			
				
 
		
	
		
			
				- behaviour ( gen_server ) .  
		
	
		
			
				
 
		
	
		
			
				- export ( [ start_link / 1 ,  init / 1 ,  handle_cast / 2 ,  handle_call / 3 ) .  
		
	
		
			
				- export ( [ start_link / 1 ,  init / 1 ,  handle_cast / 2 ,  handle_call / 3 , terminate / 2  ]) .  
		
	
		
			
				- export ( [ get_tick / 0 ] ) .  
		
	
		
			
				
 
		
	
		
			
				- record ( state ,  { database_file  =  none ,  database  =  none ,  tick_start  =  none } ) .  
		
	
	
		
			
				
					
						
							
								 
						
						
							
								 
						
						
					 
				
			
			@ -46,37 +46,67 @@ init([DatabaseFile]) ->
 
		
	
		
			
				  end . 
 
		
	
		
			
				
 
		
	
		
			
				init_db ( Database )  - >  
		
	
		
			
				  ok  =  sqlite3 : create_table ( Database ,  divisions ,  [ { division ,  integer ,  [ not_null ] } ] , 
 
		
	
		
			
				  ok  =  sqlite3 : create_table ( Database ,  divisions ,  [ { division ,  text ,  [ not_null ] } ] , 
 
		
	
		
			
				                            [ { primary_key ,  [ division ] } ] ) , 
 
		
	
		
			
				
 
		
	
		
			
				  ok  =  sqlite3 : create_table ( Database ,  fields ,  [ { field ,  text ,  [ not_null ] } , 
 
		
	
		
			
				                                               { serial ,  text ,  [ unique ] } , 
 
		
	
		
			
				                                               { division ,  text } , 
 
		
	
		
			
				                                               { other ,  text } ] , 
 
		
	
		
			
				                            [ { primary_key ,  [ field ] } , 
 
		
	
		
			
				                             { foreign_key ,  { [ division ] ,  divisions ,  [ division ] ,  " ON DELETE SET NULL " } } , 
 
		
	
		
			
				                             { check ,  " other IN ('testing', 'skills') " } ] ) , 
 
		
	
		
			
				
 
		
	
		
			
				  ok  =  sqlite3 : create_table ( Database ,  teams ,  [ { team ,  text ,  [ not_null ] } , 
 
		
	
		
			
				                                              { division ,  integer } , 
 
		
	
		
			
				                                              { division ,  text } , 
 
		
	
		
			
				                                              { inspection ,  blob } ] , 
 
		
	
		
			
				                            [ { primary_key ,  [ team ] } , 
 
		
	
		
			
				                             { foreign_key ,  { [ division ] ,  divisions ,  [ division ] ,  " " } } ] ) , 
 
		
	
		
			
				                             { foreign_key ,  { [ division ] ,  divisions ,  [ division ] ,  " ON DELETE SET NULL "} } ] ) , 
 
		
	
		
			
				
 
		
	
		
			
				  ok  =  sqlite3 : create_table ( Database ,  matches ,  [ { division ,  integer } , 
 
		
	
		
			
				  ok  =  sqlite3 : create_table ( Database ,  matches ,  [ { division ,  text } , 
 
		
	
		
			
				                                                { type ,  text ,  [ not_null ] } , 
 
		
	
		
			
				                                                { number ,  integer ,  [ not_null ] } , 
 
		
	
		
			
				                                                { instance ,  integer ,  [ not_null ] } ] , 
 
		
	
		
			
				                            [ { primary_key ,  [ division ,  type ,  number ,  instance ] } , 
 
		
	
		
			
				                                                { number ,  integer ,  [ not_null ] } ] , 
 
		
	
		
			
				                            [ { primary_key ,  [ division ,  type ,  number ] } , 
 
		
	
		
			
				                             { check ,  " type IN ('practice', 'qualification', 'elimination', 'final') " } , 
 
		
	
		
			
				                             { foreign_key ,  { [ division ] ,  divisions ,  [ division ] ,  " ON DELETE CASCADE " } } ] ) , 
 
		
	
		
			
				
 
		
	
		
			
				  ok  =  sqlite3 : create_table ( Database ,  match_teams ,  [ { division ,  integer,  [ not_null ]  } , 
 
		
	
		
			
				  ok  =  sqlite3 : create_table ( Database ,  match_teams ,  [ { division ,  text } , 
 
		
	
		
			
				                                                    { type ,  text ,  [ not_null ] } , 
 
		
	
		
			
				                                                    { number ,  integer ,  [ not_null ] } , 
 
		
	
		
			
				                                                    { instance ,  integer ,  [ not_null ] } , 
 
		
	
		
			
				                                                    { side ,  text ,  [ not_null ] } ,  
 
		
	
		
			
				                                                    { anum ,  integer ,  [ not_null ] } , 
 
		
	
		
			
				                                                    { team ,  text ,  [ not_null ] } ] , 
 
		
	
		
			
				                            [ { primary_key ,  [ division ,  type ,  number ,  instance,   side,  anum ] } , 
 
		
	
		
			
				                            [ { primary_key ,  [ division ,  type ,  number ,  ,  anum ] } , 
 
		
	
		
			
				                             { check ,  " side IN ('red', 'blue') " } , 
 
		
	
		
			
				                             { check ,  " anum >= 1 AND anum <= 2 " } , 
 
		
	
		
			
				                             { foreign_key ,  { [ division ,  type ,  number ,  instance ] ,  matches ,  [ division ,  type ,  number ,  instance ] ,  " ON DELETE CASCADE " } } , 
 
		
	
		
			
				                             { check ,  " type IN ('practice', 'qualification') " } , 
 
		
	
		
			
				                             { foreign_key ,  { [ division ,  type ,  number ] ,  matches ,  [ division ,  type ,  number ] ,  " ON DELETE CASCADE " } } , 
 
		
	
		
			
				                             { foreign_key ,  { [ team ] ,  teams ,  [ team ] ,  " " } } ] ) , 
 
		
	
		
			
				
 
		
	
		
			
				  ok  =  sqlite3 : create_table ( Database ,  match_scores ,  [ { division ,  integer } , 
 
		
	
		
			
				  ok  =  sqlite3 : create_table ( Database ,  elim_alliances ,  [ { division ,  text } , 
 
		
	
		
			
				                                                       { type ,  text ,  [ not_null ] } , 
 
		
	
		
			
				                                                       { number ,  integer ,  [ not_null ] } , 
 
		
	
		
			
				                                                       { side ,  text ,  [ not_null ] } , 
 
		
	
		
			
				                                                       { anum ,  integer ,  [ not_null ] } ] , 
 
		
	
		
			
				                            [ { primary_key ,  [ division ,  type ,  number ,  side ] } , 
 
		
	
		
			
				                             { check ,  " side IN ('red', 'blue') " } , 
 
		
	
		
			
				                             { check ,  " type == 'elimination' " } , 
 
		
	
		
			
				                             { foreign_key ,  { [ division ,  type ,  number ] ,  matches ,  [ division ,  type ,  number ] ,  " ON DELETE CASCADE " } } , 
 
		
	
		
			
				                             { foreign_key ,  { [ division ,  anum ] ,  alliances ,  [ division ,  number ] ,  " " } } ] ) , 
 
		
	
		
			
				
 
		
	
		
			
				  ok  =  sqlite3 : create_table ( Database ,  finals_alliances ,  [ { division ,  text } , 
 
		
	
		
			
				                                                         { type ,  text ,  [ not_null ] } , 
 
		
	
		
			
				                                                         { number ,  integer ,  [ not_null ] } , 
 
		
	
		
			
				                                                         { side ,  text ,  [ not_null ] } , 
 
		
	
		
			
				                                                         { adiv ,  text ,  [ not_null ] } ] , 
 
		
	
		
			
				                            [ { primary_key ,  [ division ,  type ,  number ,  side ] } , 
 
		
	
		
			
				                             { check ,  " side IN ('red', 'blue') " } , 
 
		
	
		
			
				                             { check ,  " division IS NULL " } , 
 
		
	
		
			
				                             { check ,  " type == 'final' " } , 
 
		
	
		
			
				                             { foreign_key ,  { [ division ,  type ,  number ] ,  matches ,  [ division ,  type ,  number ] ,  " ON DELETE CASCADE " } } , 
 
		
	
		
			
				                             { foreign_key ,  { [ adiv ] ,  winners ,  [ division ] ,  " " } } ] ) , 
 
		
	
		
			
				
 
		
	
		
			
				  ok  =  sqlite3 : create_table ( Database ,  match_scores ,  [ { division ,  text } , 
 
		
	
		
			
				                                                     { type ,  text ,  [ not_null ] } , 
 
		
	
		
			
				                                                     { number ,  integer ,  [ not_null ] } , 
 
		
	
		
			
				                                                     { instance ,  integer ,  [ not_null ] } , 
 
		
	
	
		
			
				
					
						
							
								 
						
						
							
								 
						
						
					 
				
			
			@ -103,32 +133,34 @@ init_db(Database) ->
 
		
	
		
			
				                             { check ,  " red_2 IS NULL OR red_2 IN ('dq', 'no show') " } , 
 
		
	
		
			
				                             { check ,  " blue_1 IS NULL OR blue_1 IN ('dq', 'no show') " } , 
 
		
	
		
			
				                             { check ,  " blue_2 IS NULL OR blue_2 IN ('dq', 'no show') " } , 
 
		
	
		
			
				                             { foreign_key ,  { [ division ,  type ,  number , instance  ],  matches ,  [ division ,  type ,  number ,  instance ] ,  " ON DELETE CASCADE " } } ] ) , 
 
		
	
		
			
				                             { foreign_key ,  { [ division ,  type ,  number ,  matches ,  [ division ,  type ,  number ] ,  " ON DELETE CASCADE " } } ] ) , 
 
		
	
		
			
				
 
		
	
		
			
				  ok  =  sqlite3 : create_table ( Database ,  match_states ,  [ { division ,  integer } , 
 
		
	
		
			
				  ok  =  sqlite3 : create_table ( Database ,  match_states ,  [ { division ,  text } , 
 
		
	
		
			
				                                                     { type ,  text ,  [ not_null ] } , 
 
		
	
		
			
				                                                     { number ,  integer ,  [ not_null ] } , 
 
		
	
		
			
				                                                     { instance ,  integer ,  [ not_null ] } , 
 
		
	
		
			
				                                                     { tick ,  integer ,  [ not_null ] } , 
 
		
	
		
			
				                                                     { state ,  text ,  [ not_null ] } ] , 
 
		
	
		
			
				                            [ { primary_key ,  [ division ,  type ,  number ,  instance,   tick] } , 
 
		
	
		
			
				                             { foreign_key ,  { [ division ,  type ,  number , instance  ],  matches ,  [ division ,  type ,  number ,  instance ] ,  " ON DELETE CASCADE " } } ] ) , 
 
		
	
		
			
				                            [ { primary_key ,  [ division ,  type ,  number ,  ] } , 
 
		
	
		
			
				                             { foreign_key ,  { [ division ,  type ,  number ,  matches ,  [ division ,  type ,  number ] ,  " ON DELETE CASCADE " } } ] ) , 
 
		
	
		
			
				
 
		
	
		
			
				  ok  =  sqlite3 : create_table ( Database ,  alliances ,  [ { division ,  integer ,  [ not_null ] } , 
 
		
	
		
			
				                                                  { number ,  integer ,  [ not_null ] } , 
 
		
	
		
			
				                                                  { team1 ,  text ,  [ not_null ] } , 
 
		
	
		
			
				                                                  { team2 ,  text ,  [ not_null ] } ] , 
 
		
	
		
			
				  ok  =  sqlite3 : create_table ( Database ,  alliances ,  [ { division ,  text ,  [ not_null ] } , 
 
		
	
		
			
				                                                  { number ,  integer ,  [ not_null ] } ] , 
 
		
	
		
			
				                            [ { primary_key ,  [ division ,  number ] } , 
 
		
	
		
			
				                             { foreign_key ,  { [ team1] ,  teams ,  [ team  ] ,  " ON DELETE CASCADE " } } , 
 
		
	
		
			
				                             { foreign_key ,  { [ team2 ] ,  teams ,  [ team ] ,  " ON DELETE CASCADE " } } ] ) ,   
		
	
		
			
				
  
		
	
		
			
				  ok  =  sqlite3 : create_table ( Database ,  rankings ,  [ { division ,  integer ,  [ not_null ] } , 
 
		
	
		
			
				                                                 { rank ,  integer ,  [ not_null ] } , 
 
		
	
		
			
				                                                 { team ,  text ,  [ not_null ] } ] , 
 
		
	
		
			
				                            [ { primary_key ,  [ division ,  rank ] } , 
 
		
	
		
			
				                             { foreign_key ,  { [ division ],  divisions ,  [ division  ] ,  " ON DELETE CASCADE " } } , 
 
		
	
		
			
				                             { foreign_key ,  { [ division ] ,  divisions ,  [ division ] ,  " ON DELETE CASCADE " } } ] ) , 
 
		
	
		
			
				
  
		
	
		
			
				  ok  =  sqlite3 : create_table ( Database ,  alliance_members ,  [ { division ,  text ,  [ not_null ] } ,   
		
	
		
			
				                                                         { number  ,  integer ,  [ not_null ] } , 
 
		
	
		
			
				                                                         { anum  ,  integer ,  [ not_null ] } , 
 
		
	
		
			
				                                                          { team ,  text ,  [ not_null ] } ] , 
 
		
	
		
			
				                            [ { primary_key ,  [ division ,  number,  anum  ] } , 
 
		
	
		
			
				                             { foreign_key ,  { [ division , number ] ,  alliances ,  [ division ,  number  ] ,  " ON DELETE CASCADE " } } , 
 
		
	
		
			
				                             { foreign_key ,  { [ team ] ,  teams ,  [ team ] ,  " ON DELETE CASCADE " } } ] ) , 
 
		
	
		
			
				
 
		
	
		
			
				  ok  =  sqlite3 : create_table ( Database ,  winners ,  [ { division ,  text ,  [ not_null ] } , 
 
		
	
		
			
				                                                { alliance ,  integer ,  [ not_null ] } ] , 
 
		
	
		
			
				                            [ { primary_key ,  [ division ] } , 
 
		
	
		
			
				                             { foreign_key ,  { [ division ,  alliance ] ,  alliances ,  [ division ,  number ] ,  " ON DELETE CASCADE " } } ] ) , 
 
		
	
		
			
				
 
		
	
		
			
				  ok  =  sqlite3 : create_table ( Database ,  skills_scores ,  [ { team ,  text ,  [ not_null ] } , 
 
		
	
		
			
				                                                      { type ,  text ,  [ not_null ] } , 
 
		
	
		
			
				                                                      { attempt ,  integer ,  [ not_null ] } , 
 
		
	
	
		
			
				
					
						
							
								 
						
						
							
								 
						
						
					 
				
			
			@ -163,21 +195,21 @@ add_teams(Database, Teams) ->
 
		
	
		
			
				
 
		
	
		
			
				assign_divisions ( Database ,  Teams )  - >  
		
	
		
			
				  SQL  =  lists : append ( [ " BEGIN;  " , 
 
		
	
		
			
				                      lists : append ( [ io_lib : format ( " UPDATE teams SET division =  ~p ~s ';  " ,  [ Division ,  Team ] )  | |  { Team ,  Division }  < -  Teams ] ) , 
 
		
	
		
			
				                      lists : append ( [ io_lib : format ( " UPDATE teams SET division =  '~s '   WHERE team = '~s ';  " ,  [ Division ,  Team ] )  | |  { Team ,  Division }  < -  Teams ] ) , 
 
		
	
		
			
				                      " COMMIT; " ] ) , 
 
		
	
		
			
				  first_error ( sqlite3 : sql_exec_script ( Database ,  SQL ) ) . 
 
		
	
		
			
				
 
		
	
		
			
				write_division_matches ( Database ,  Division ,  Type ,  Matches )  - >  
		
	
		
			
				  Round  =  atom_to_list ( Type ) , 
 
		
	
		
			
				  SQL  =  lists : append ( [ " BEGIN;  " , 
 
		
	
		
			
				                      io_lib : format ( " DELETE FROM matches WHERE division =  ~p ~s '; " , 
 
		
	
		
			
				                      io_lib : format ( " DELETE FROM matches WHERE division =  '~s '   AND type = '~s '; " , 
 
		
	
		
			
				                                    [ Division ,  Round ] ) , 
 
		
	
		
			
				                      lists : append ( [ io_lib : format ( 
 
		
	
		
			
				                                      " INSERT INTO matches(division, type, number , instance) VALUES (~p , ' ~s ',  ~p , 1  );
 
		
	
		
			
				                                       INSERT  INTO  match_teams ( division ,  type ,  number ,  instance,   side,  anum ,  team )  VALUES  ( ~ p ,  '~s' ,  ~ p ,  1 ,  'red' ,   1 ,  '~s' ) ; 
 
		
	
		
			
				                                       INSERT  INTO  match_teams ( division ,  type ,  number ,  instance,   side,  anum ,  team )  VALUES  ( ~ p ,  '~s' ,  ~ p ,  1 ,  'red' ,   2 ,  '~s' ) ; 
 
		
	
		
			
				                                       INSERT  INTO  match_teams ( division ,  type ,  number ,  instance,   side,  anum ,  team )  VALUES  ( ~ p ,  '~s' ,  ~ p ,  1 ,  'blue' ,  1 ,  '~s' ) ; 
 
		
	
		
			
				                                       INSERT  INTO  match_teams ( division ,  type ,  number ,  instance,   side,  anum ,  team )  VALUES  ( ~ p ,  '~s' ,  ~ p ,  1 ,  'blue' ,  2 ,  '~s' ) ; " , 
 
		
	
		
			
				                                      " INSERT INTO matches(division, type, number ) VALUES ('~s ', ' ~s ',  ~p  );
 
		
	
		
			
				                                       INSERT  INTO  match_teams ( division ,  type ,  number ,  ,  anum ,  team )  VALUES  ( '~s' ,  '~s' ,  ~ p ,  'red' ,   1 ,  '~s' ) ; 
 
		
	
		
			
				                                       INSERT  INTO  match_teams ( division ,  type ,  number ,  ,  anum ,  team )  VALUES  ( '~s' ,  '~s' ,  ~ p ,  'red' ,   2 ,  '~s' ) ; 
 
		
	
		
			
				                                       INSERT  INTO  match_teams ( division ,  type ,  number ,  ,  anum ,  team )  VALUES  ( '~s' ,  '~s' ,  ~ p ,  'blue' ,  1 ,  '~s' ) ; 
 
		
	
		
			
				                                       INSERT  INTO  match_teams ( division ,  type ,  number ,  ,  anum ,  team )  VALUES  ( '~s' ,  '~s' ,  ~ p ,  'blue' ,  2 ,  '~s' ) ; " , 
 
		
	
		
			
				                                      [ Division ,  Round ,  N ,  Division ,  Round ,  N ,  B1 ,  Division ,  Round ,  N ,  B2 ,  Division ,  Round ,  N ,  R1 ,  Division ,  Round ,  N ,  R2 ] ) 
 
		
	
		
			
				                                    | |  { N ,  [ B1 ,  B2 ,  R1 ,  R2 ] }  < -  lists : enumerate ( Matches ) ] ) , 
 
		
	
		
			
				                      " COMMIT; " ] ) , 
 
		
	
	
		
			
				
					
						
						
						
							
								 
						
					 
				
			
			@ -187,17 +219,14 @@ index_of(Item, List) -> index_of(Item, List, 1).
 
		
	
		
			
				index_of ( Item ,  [ Element  |  _ ] ,  N )  when  Item  ==  Element  - >  N ;  
		
	
		
			
				index_of ( Item ,  [ _  |  List ] ,  N )  - >  index_of ( Item ,  List ,  N + 1 ) .  
		
	
		
			
				
 
		
	
		
			
				first_empty ( [ Num1 ,  Num2  |  Nums ] )  when  Num2  ==  ( Num1  +  1 )  - >  first_empty ( [ Num2  |  Nums ] ) ;  
		
	
		
			
				first_empty ( [ Num  |  _ ] )  - >  Num  +  1 .  
		
	
		
			
				
 
		
	
		
			
				get_column ( _ ,  [ ] ,  _ )  - >  [ ] ;  
		
	
		
			
				get_column ( _ ,  _ ,  [ ] )  - >  [ ] ;  
		
	
		
			
				get_column ( Name ,  Columns ,  Rows )  - >  
		
	
		
			
				  Index  =  index_of ( Name ,  Columns ) , 
 
		
	
		
			
				  [ element ( Index ,  Row )  | |  Row  < -  Rows ] . 
 
		
	
		
			
				
 
		
	
		
			
				handle_call ( get_tick_start ,  _ ,  State )  - >  
		
	
		
			
				  { reply ,  State #state.tick_start ,  State } ; 
 
		
	
		
			
				handle_call ( { time_sync ,  T0 } ,  _ ,  State )  - >  
		
	
		
			
				  { reply ,  { T0 ,  get_tick ( State ) } ,  State } ; 
 
		
	
		
			
				handle_call ( { new_db ,  DatabaseFile } ,  _ ,  State )  - >  
		
	
		
			
				  ok  =  if  State #state.database  =:=  none  - >  ok ; 
 
		
	
		
			
				          true  - >  sqlite3 : close ( State #state.database ) 
 
		
	
	
		
			
				
					
						
						
						
							
								 
						
					 
				
			
			@ -218,6 +247,52 @@ handle_call({load_db, DatabaseFile}, _, State) ->
 
		
	
		
			
				                                    end , 
 
		
	
		
			
				                               open_db ( DatabaseFile ) 
 
		
	
		
			
				                          end , 
 
		
	
		
			
				
 
		
	
		
			
				  %  Publish  division / { division }  with  fields 
 
		
	
		
			
				  [ _ ,  { rows ,  Divisions } ]  =  sqlite3 : sql_exec ( Database ,  " SELECT division FROM divisions; " ) , 
 
		
	
		
			
				  [ _ ,  { rows ,  DivisionFields } ]  =  sqlite3 : sql_exec ( Database ,  " SELECT field, division FROM fields WHERE division IS NOT NULL; " ) , 
 
		
	
		
			
				  DivisionMessages  =  [ { list_to_binary ( io_lib : format ( " division/ ~s " ,  [ Name ] ) ) , 
 
		
	
		
			
				                       jsone : encode ( #{ < < " fields " > >  = > 
 
		
	
		
			
				                                      lists : filtermap ( fun ( { Field ,  FieldDivision } )  - > 
 
		
	
		
			
				                                                          if  FieldDivision  ==  Name  - > 
 
		
	
		
			
				                                                               { 'true' ,  Field } ; 
 
		
	
		
			
				                                                             true  - >  false 
 
		
	
		
			
				                                                          end 
 
		
	
		
			
				                                                      end ,  DivisionFields ) 
 
		
	
		
			
				                                     } ) } 
 
		
	
		
			
				                      | |  { Name }  < -  Divisions ] , 
 
		
	
		
			
				
 
		
	
		
			
				  %  Publish  team / { team }  with  inspection 
 
		
	
		
			
				  %  Publish  division / { division } / teams  with  team  list 
 
		
	
		
			
				  [ _ ,  { rows ,  Teams } ]  =  sqlite3 : sql_exec ( Database ,  " SELECT team, division, inspection FROM teams " ) , 
 
		
	
		
			
				  TeamsMessages  =  [ { list_to_binary ( io_lib : format ( " team/ ~s " ,  [ Team ] ) ) , 
 
		
	
		
			
				                    jsone : encode ( #{ < < " inspection " > >  = >  Inspection } ) } 
 
		
	
		
			
				                   | |  { Team ,  _ ,  Inspection }  < -  Teams ] , 
 
		
	
		
			
				  DivisionTeams  =  [ { list_to_binary ( io_lib : format ( " division/ ~s /teams " ,  [ Name ] ) ) , 
 
		
	
		
			
				                    jsone : encode ( lists : filtermap ( fun ( { Team ,  Div ,  _ } )  - > 
 
		
	
		
			
				                                                     if  Div  ==  Name  - > 
 
		
	
		
			
				                                                          { 'true' ,  Team } ; 
 
		
	
		
			
				                                                        true  - > 
 
		
	
		
			
				                                                          false 
 
		
	
		
			
				                                                     end 
 
		
	
		
			
				                                                 end ,  Teams ) ) } 
 
		
	
		
			
				                   | |  { Name }  < -  Divisions ] , 
 
		
	
		
			
				
 
		
	
		
			
				  [ _ ,  { rows ,  Alliances } ]  =  sqlite3 : sql_exec ( Database ,  " SELECT team, div " ) , 
 
		
	
		
			
				
 
		
	
		
			
				  %  Read  matches  and  match_teams  tables  and  publish  to  match / { div } / { round } / { number } 
 
		
	
		
			
				  %  Read  match_sates  and  publish  to  match / { div } / { round } / { number } / state 
 
		
	
		
			
				  %  Read  match_scores  and  publish  to  match / { div } / { round } / { number } / state 
 
		
	
		
			
				  %  Read  rankings  and  publish  divisions / { div } / rankings 
 
		
	
		
			
				  %  Read  skills  scores  and  publish  team / { team } / skills 
 
		
	
		
			
				  %  Read  alliances  and  publish  to  division / { div } / alliances 
 
		
	
		
			
				
 
		
	
		
			
				
 
		
	
		
			
				  %  DEBUG  PRINTS 
 
		
	
		
			
				  io : fwrite ( " DIVISIONS:  ~p ~n " ,  [ DivisionMessages ] ) , 
 
		
	
		
			
				  io : fwrite ( " TEAMS:  ~p ~n " ,  [ TeamsMessages ] ) , 
 
		
	
		
			
				  io : fwrite ( " INSPECTION:  ~p ~n " ,  [ DivisionTeams ] ) , 
 
		
	
		
			
				
 
		
	
		
			
				  { reply ,  ok ,  State #state { database_file  =  DatabaseFile ,  database  =  Database ,  tick_start  =  TickStart } } ; 
 
		
	
		
			
				handle_call ( close_db ,  _ ,  State )  - >  
		
	
		
			
				  if  State #state.database  =:=  none  - >  { reply ,  ok ,  State } ; 
 
		
	
	
		
			
				
					
						
						
						
							
								 
						
					 
				
			
			@ -234,8 +309,10 @@ handle_call({assign_divisions, Teams}, _, State) ->
 
		
	
		
			
				  { reply ,  ok ,  State } ; 
 
		
	
		
			
				handle_call ( split_teams ,  _ ,  State )  - >  
		
	
		
			
				  Teams  =  [ X | | { _ , X }  < -  lists : sort ( [ { rand : uniform ( ) ,  N }  | |  N  < -  get_teams ( State #state.database ) ] ) ] , 
 
		
	
		
			
				  [ { columns ,  _ } ,  { rows ,  Divisions } ]  =  sqlite3 : sql_exec ( State #state.database ,  " SELECT division FROM divisions; " ) , 
 
		
	
		
			
				  Assignments  =  [ { Team ,  ( I  rem  length ( Divisions ) )  +  1 }  | |  { I ,  Team }  < -  lists : enumerate ( Teams ) ] , 
 
		
	
		
			
				  [ { columns ,  Cols } ,  { rows ,  Rows } ]  =  sqlite3 : sql_exec ( State #state.database ,  " SELECT division FROM divisions; " ) , 
 
		
	
		
			
				  Divisions  =  get_column ( " division " ,  Cols ,  Rows ) , 
 
		
	
		
			
				  Assignments  =  [ { Team ,  lists : nth ( ( I  rem  length ( Divisions ) )  +  1 ,  Divisions ) } 
 
		
	
		
			
				                 | |  { I ,  Team }  < -  lists : enumerate ( Teams ) ] , 
 
		
	
		
			
				  ok  =  assign_divisions ( State #state.database ,  Assignments ) , 
 
		
	
		
			
				  { reply ,  ok ,  State } ; 
 
		
	
		
			
				handle_call ( { delete_teams ,  Removed } ,  _ ,  State )  - >  
		
	
	
		
			
				
					
						
						
						
							
								 
						
					 
				
			
			@ -253,31 +330,38 @@ handle_call({match_score, Division, Round, Number, Instance, Red, Blue, Score},
 
		
	
		
			
				                 end , 
 
		
	
		
			
				  ok  =  first_error ( sqlite3 : sql_exec_script ( State #state.database ,  lists : append ( 
 
		
	
		
			
				                                                                   [ io_lib : format ( " INSERT INTO match_scores(division, type, number, instance, history, red, blue, score) VALUES( ~p , ' ~s ',  ~p ,  ~p ,  ~p ,  ~p ,  ~p , ' ~s '); " ,  [ Division ,  atom_to_list ( Round ) ,  Number ,  Instance ,  NextInstance ,  Red ,  Blue ,  Score ] ) , 
 
		
	
		
			
				                                                                    io_lib : format ( " INSERT INTO match_states(division, type, number,  instance,  tick, state) VALUES(~p , ' ~s ' , ~p  , ~p ,  ~p , ' ~s '); " ,  [ Division ,  atom_to_list ( Round ) ,  Number ,  Instance ,  get_tick ( State ) ,  " scored " ] ) ] ) ) ) , 
 
		
	
		
			
				                                                                    io_lib : format ( " INSERT INTO match_states(division, type, number,  tick, state) VALUES(~p , ' ~s ' , ~p ,  ~p , ' ~s '); " ,  [ Division ,  atom_to_list ( Round ) ,  Number ,  get_tick ( State ) ,  " scored " ] ) ] ) ) ) , 
 
		
	
		
			
				  { reply ,  ok ,  State } ; 
 
		
	
		
			
				handle_call ( { match_state ,  Division ,  Round ,  Number ,  Instance,   MatchState} ,  _ ,  State )  - >  
		
	
		
			
				handle_call ( { match_state ,  Division ,  Round ,  Number ,  } ,  _ ,  State )  - >  
		
	
		
			
				  { rowid ,  _ }  =  sqlite3 : sql_exec ( State #state.database , 
 
		
	
		
			
				                                io_lib : format ( " INSERT INTO match_states(division, type, number,  instance,  tick, state) VALUES(~p , ' ~s ' , ~p  , ~p ,  ~p , ' ~s '); " , 
 
		
	
		
			
				                                              [ Division ,  atom_to_list ( Round ) ,  Number ,  Instance ,  get_tick ( State ) ,  MatchState ] ) ) , 
 
		
	
		
			
				                                io_lib : format ( " INSERT INTO match_states(division, type, number,  tick, state) VALUES(~p , ' ~s ' , ~p ,  ~p , ' ~s '); " , 
 
		
	
		
			
				                                              [ Division ,  atom_to_list ( Round ) ,  Number ,  get_tick ( State ) ,  MatchState ] ) ) , 
 
		
	
		
			
				  { reply ,  ok ,  State } ; 
 
		
	
		
			
				
 
		
	
		
			
				handle_call ( add_division ,  _ ,  State )  - >  
		
	
		
			
				  [ { columns ,  Columns } ,  { rows ,  Rows } ]  =  sqlite3 : sql_exec ( State #state.database ,  " SELECT division FROM divisions; " ) , 
 
		
	
		
			
				  Divisions  =  lists : sort ( get_column ( " division " ,  Columns ,  Rows ) ) , 
 
		
	
		
			
				  NextDivision  =  first_empty ( [ 0  |  Divisions ] ) , 
 
		
	
		
			
				  { rowid ,  _ }  =  sqlite3 : sql_exec ( State #state.database , 
 
		
	
		
			
				                                io_lib : format ( " INSERT INTO divisions(division) VALUES( ~p ); " , 
 
		
	
		
			
				                                              [ NextDivision ] ) ) , 
 
		
	
		
			
				handle_call ( { add_division ,  Name } ,  _ ,  State )  - >  
		
	
		
			
				  { rowid ,  _ }  =  sqlite3 : sql_exec ( 
 
		
	
		
			
				                 State #state.database , 
 
		
	
		
			
				                 io_lib : format ( " INSERT INTO divisions(division) VALUES(' ~s '); " , 
 
		
	
		
			
				                               [ Name ] ) ) , 
 
		
	
		
			
				  { reply ,  ok ,  State } ; 
 
		
	
		
			
				handle_call ( { delete_division ,  Division } ,  _ ,  State )  - >  
		
	
		
			
				  ok  =  first_error ( sqlite3 : sql_exec_script ( State #state.database ,  io_lib : format ( " UPDATE teams SET division = NULL WHERE division =  ~p ; DELETE FROM divisions WHERE division =  ~p ; " ,  [ Division ,  Division ] ) ) ) , 
 
		
	
		
			
				  ok  =  first_error ( sqlite3 : sql_exec_script ( 
 
		
	
		
			
				                     State #state.database , 
 
		
	
		
			
				                     io_lib : format ( 
 
		
	
		
			
				                       " UPDATE teams SET division = NULL WHERE division = ' ~s '; 
 
		
	
		
			
				                       DELETE  FROM  divisions  WHERE  division  =  '~s' ; " , 
 
		
	
		
			
				                       [ Division ,  Division ] ) ) ) , 
 
		
	
		
			
				  { reply ,  ok ,  State } ; 
 
		
	
		
			
				handle_call ( { generate_division ,  Division ,  Round ,  Size ,  Seed } ,  _ ,  State )  - >  
		
	
		
			
				handle_call ( { generate_division ,  Division ,  Round ,  Size ,  _ ,  State )  - >  
		
	
		
			
				  Teams  =  get_div_teams ( State #state.database ,  Division ) , 
 
		
	
		
			
				  io : fwrite ( " Generating division  ~p / ~s  with teams  ~p , size  ~p , and seed  ~p ~n " ,  [ Division ,  Round ,  Teams ,  Size ,  Seed ] ) , 
 
		
	
		
			
				  Matches  =  schedule : create ( Seed ,  Size ,  Teams ) , 
 
		
	
		
			
				  ok  =  write_division_matches ( State #state.database ,  Division ,  Round ,  Matches ) , 
 
		
	
		
			
				  { reply ,  ok ,  State } . 
 
		
	
		
			
				  Seed  =  rand : uniform ( 10000000 ) , 
 
		
	
		
			
				  io : fwrite ( " Generating division  ~s / ~s  with teams  ~p , size  ~p , and seed  ~p ~n " ,  [ Division ,  Round ,  Teams ,  Size ,  Seed ] ) , 
 
		
	
		
			
				  case  schedule : create ( Seed ,  Size ,  Teams )  of 
 
		
	
		
			
				    error  - >  { reply ,  error ,  State } ; 
 
		
	
		
			
				    Matches  - >  { reply ,  write_division_matches ( State #state.database ,  Division ,  Round ,  Matches ) ,  State } 
 
		
	
		
			
				  end ; 
 
		
	
		
			
				handle_call ( _ ,  _ ,  State )  - >  
		
	
		
			
				  { reply ,  nofunc ,  State } . 
 
		
	
		
			
				
 
		
	
		
			
				get_div_teams ( Database ,  Division )  - >  
		
	
		
			
				  [ { columns ,  Columns } ,  { rows ,  Rows } ]  =  sqlite3 : sql_exec ( Database ,  io_lib : format ( " SELECT team FROM teams WHERE division =  ~p ; " ,  [ Division ] ) ) , 
 
		
	
	
		
			
				
					
						
						
						
							
								 
						
					 
				
			
			@ -292,4 +376,14 @@ handle_cast(_, State) ->
 
		
	
		
			
				
 
		
	
		
			
				get_tick ( State )  - >  erlang : monotonic_time ( millisecond )  -  State #state.tick_start .  
		
	
		
			
				get_tick ( )  - >  
		
	
		
			
				  erlang : monotonic_time ( millisecond )  -  gen_server : call ( ? MODULE ,  get_tick_start ) . 
 
		
	
		
			
				  T0  =  erlang : monotonic_time ( millisecond ) , 
 
		
	
		
			
				  { T0 ,  T1 }  =  gen_server : call ( ? MODULE ,  { time_sync ,  T0 } ) , 
 
		
	
		
			
				  T2  =  erlang : monotonic_time ( millisecond ) , 
 
		
	
		
			
				  Offset  =  trunc ( ( ( T1 - T0 )  +  ( T1 - T2 ) ) / 2 ) , 
 
		
	
		
			
				  NetworkDelay  =  trunc ( ( T2 - T0 ) / 2 ) , 
 
		
	
		
			
				  ServerTick  =  T2  +  Offset  +  NetworkDelay , 
 
		
	
		
			
				  { Offset ,  ServerTick } . 
 
		
	
		
			
				
 
		
	
		
			
				terminate ( _ ,  State )  - >  
		
	
		
			
				  DB  =  State #state.database , 
 
		
	
		
			
				  ok  =  if  DB  =:=  none  - >  ok ;  true  - >  sqlite3 : close ( DB )  end .