AOJ2087 Speed

問題リンク Speed

  • 概要

トランプの「スピード」をプレイするロボットA, Bがいる。このロボットたちの動作をシミュレートしてどちらが勝つかを判定せよ。
「スピード」は手札が4枚、場が2か所、デッキを使い、2人が向かい合って遊ぶ。場に出ている一番上のカードに書いてある数字と隣り合っている数字が手札にあるとき、そのカードをその場の一番上に置くことができる。2人が同時に置こうとしたとき、早く置いたもの勝ちである。置けなかった人は出そうとしていたカードを元の位置に戻さなければならない。手札に空きができると、デッキからカードを1枚補充する必要がある。デッキが無い場合は補充する必要はない。2人とも、場に置くカードが無くなった場合はデッキから1枚取って同時にそれぞれ右側の場へカードを置く。デッキが空の場合は手札から任意の1枚を置く。早く手札、デッキを空にした者が勝ちである。

ロボットはそれぞれNA枚、NB枚のカードが与えられる。そしてロボットは次のようなアルゴリズムで動作する。
・ロボットは与えられたカードの順にカードを引く
 ・使うカードはトランプ1組を分けて使うので、全く同じカードが存在しない

・ゲームの初期時、ロボットはカードを4枚引き、右から左の順に手札の場に置く。与えられたカードが4枚未満の場合は置けるだけ置く。この動作が終わると、ロボットは互いに同期を始める。ロボットが各々の右側の場へカードを同時に置いてゲームがスタートする。出すカードはデッキから取る。デッキが空の場合、手札の一番右側に置いたカードを出す。

・この後、上記の一般的なスピードのルールにのっとってロボットは動き続ける
 ・ロボットが場にカードを出したとき、デッキが空でないならば、デッキからカードを1枚取って、手札の空いた場所へ置く
 ・ロボットが同じ場へカードを出そうとしている場合、完全に置ききった方のみが置ける。置けなかった場合は、ただちに出そうとしたカードを元の位置へ戻す
 ・ロボットは、場に出ている一番上のカードと隣り合っているカードがある場合のみ、カードを置く動作を開始することができる
 ・もし、ロボットが同じ場へカードを出そうとしている場合で、なおかつ、置ききる時間も同時ならば、ロボットから見て、左側の場へ出そうとしているロボットのみが置くことができる。
 ・手札に、場に出すことができるカードが複数ある場合、右側の場へ置けるカードを優先する。それでも候補が複数あるならば、その中で一番弱いカードを出す

・どちらのロボットも場に出すカードがない状態に陥った場合、ゲーム開始時の、同時にカードを出す動作をしてゲームが再開される。

・どちらかのロボットのカードが無くなった場合ゲームが終了する
 ・カードが無くなった方のロボットが勝つ
 ・もし、同時にカードが無くなった場合、最後に出したカードが強い方が勝つ

カードの強さは、その数字、その柄の順で強さを決定する。数字は
A > K > Q > J > X(10) > 9 > ... > 2
の強さの序列がある。数字が同じ場合、柄の強さで比較する。
S > H > D > C
が柄の強さの序列である。
カードが隣り合っているとは、その数字が隣接しているかどうかである。AとKは隣接しているとみなす。

ロボットは各動作をする場合、以下の通りの時間がかかる
・デッキから手札にカードを補充する動作 300ms
・右側の場へカードを置く動作 500ms
・左側の場へカードを置く動作 700ms
・カードを元の場所へ戻す動作 500ms

カードを元の場所へ戻す動作は、カードを置こうとする動作の進捗に関わらず常に500msかかる。また、カードを置こうとする動作がキャンセルされたその時点から500msかかる。

  • 解法

目がくらむようなシミュレーション実装問題です。
ロボットの動作にかかる時間ですが、全て100msの倍数なので100で割ってしまいましょう。
そして、各ロボットに対して、時間tに動作eventを行うという情報を持たせ、時間を1msずつ動かしてシミュレートしました。
ロボットの動作は以下のように区切ってみました
WAIT: 場に出せるカードがないので待機
THINK: 手札に空きがあれば補充し、そうでなければ場に出すカードを決定する
BACK: カードを出そうとしたら置かれてしまったので、元の位置に戻そうとしている状態
PUT: 場にカードを置きにいっている状態
WIN: カードを出し切った
DRAW: デッキからカードを補充しようとしている状態
あとは、これを使って色々気を配りながら実装していきます。
相当苦戦しました。
作ったソースはかなり冗長な部分が多いのですが、もはや整形する気力がありません。

  • ソース
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;

//Speed
public class AOJ2087 {

	class C{
		int suit, rank;
		String str;
		public C(String s) {
			str = s;
			suit = s.charAt(0);
			char c = s.charAt(1);
			rank = c=='A'?1:c=='K'?13:c=='Q'?12:c=='J'?11:c=='X'?10:(c-'0');
		}
		boolean isNeighbor(C c){
			int r1 = rank-1, r2 = c.rank-1;
			return (r1+1)%13==r2 || (r2+1)%13==r1;
		}
		boolean isStrong(C c){
			int r1 = rank, r2 = c.rank;
			if(r1==1)r1=14;
			if(r2==1)r2=14;
			return r1!=r2?r1>r2:suit>c.suit;
		}
		boolean same(C c){
			return suit==c.suit&&rank==c.rank;
		}
	}
	
	final int WAIT = 0, THINK = 1, PUT = 2, BACK = 3, WIN = 4, DRAW = 5;
	
	class R{
		int id;
		C lastCard, nowCard;
		int pos, to, t, event, num;
		Queue<C> deck;
		boolean leftSide;
		C[] place;
		public R(int id) {
			place = new C[4];
			this.id = id;
			lastCard = nowCard = null;
			deck = new LinkedList<C>();
		}
		void put(){
			num--;
			table[to] = nowCard;
			nowCard = null;
			lastCard = table[to];
		}
		void putPrepare(){
			int j = choice();
			nowCard = place[j];
			place[j] = null;
			pos = j;
			event = PUT;
			if(table[id].isNeighbor(nowCard)){
				to = id;
				t+=5;
				leftSide = false;
			}
			else{
				to = 1-id;
				t+=7;
				leftSide = true;
			}
		}
		void back(){
			place[pos] = nowCard;
		}
		void init(){
			num = Math.min(4, deck.size());
			for(int i=0;i<num;i++)place[i]=deck.poll();
		}
		void restart(){
			if(deck.isEmpty()){
				num--;
				for(int i=0;i<4;i++)if(place[i]!=null){
					table[id] = place[i];
					lastCard  = place[i];
					place[i] = null;
					break;
				}
			}
			else{
				table[id] = deck.poll();
				lastCard = table[id];
			}
		}
		void draw(){
			for(int i=0;i<4;i++)if(place[i]==null){
				num++;
				place[i] = deck.poll();
				break;
			}
		}
		int choice(){
			C c = null;
			int r = -1;
			for(int i=0;i<4;i++)if(place[i]!=null){
				if(place[i].isNeighbor(table[id])){
					if(c==null){
						c = place[i]; r = i;
					}
					else if(!place[i].isStrong(c)){
						c = place[i]; r = i;
					}
				}
			}
			if(r!=-1)return r;
			for(int i=0;i<4;i++)if(place[i]!=null){
				if(place[i].isNeighbor(table[1-id])){
					if(c==null){
						c = place[i]; r = i;
					}
					else if(!place[i].isStrong(c)){
						c = place[i]; r = i;
					}
				}
			}
			return r;
		}
	}
	
	R[] robot;
	C[] table;
	
	void run(){
		Scanner sc = new Scanner(System.in);
		robot = new R[2];
		table = new C[2];
		for(;;){
			int n = sc.nextInt();
			if(n==0)break;
			robot[0] = new R(0); robot[1] = new R(1);
			table[0] = table[1] = null;
			while(n--!=0)robot[0].deck.add(new C(sc.next()));
			n = sc.nextInt();
			while(n--!=0)robot[1].deck.add(new C(sc.next()));
			robot[0].init(); robot[1].init();
			robot[0].restart(); robot[1].restart();
			boolean winA = false;
			if(robot[0].num==0&&robot[1].num==0){
				winA = robot[0].lastCard.isStrong(robot[1].lastCard);
				System.out.println(winA?"A wins.":"B wins.");
				continue;
			}
			if(robot[0].num==0){
				System.out.println("A wins."); continue;
			}
			if(robot[1].num==0){
				System.out.println("B wins."); continue;
			}
			robot[0].event = THINK; robot[1].event = THINK;
			for(int T=0;;T++){
				R r0 = robot[0], r1 = robot[1];
				if(r0.t==T&&r1.t==T){
					if(r0.event == WIN && r1.event == WIN){
						winA = r0.lastCard.isStrong(r1.lastCard);
						break;
					}
					if(r0.event == WAIT && r1.event == WAIT){
						r0.restart(); r1.restart();
						r0.t++; r1.t++;
						if(r0.num==0)r0.event = WIN;
						if(r1.num==0)r1.event = WIN;
						if(r0.event==WIN && r1.event==WIN){
							winA = r0.lastCard.isStrong(r1.lastCard); break;
						}
						if(r0.event==WIN){
							winA = true; break;
						}
						if(r1.event==WIN){
							winA = false; break;
						}
						r0.event = THINK; r1.event = THINK;
						if(r0.event == THINK){
							int j = r0.choice();
							if(j==-1){
								r0.event = WAIT;
								r0.t++;
							}
							else{
								r0.putPrepare();
							}
						}
						if(r1.event == THINK){
							int j = r1.choice();
							if(j==-1){
								r1.event = WAIT;
								r1.t++;
							}
							else{
								r1.putPrepare();
							}
						}
						continue;
					}
					if(r0.event == PUT && r1.event == PUT){
						if(r0.leftSide!=r1.leftSide){
							//put Robot A
							if(r0.leftSide){
								r1.t+=5;
								r1.event = BACK;
								r0.put();
								//draw from deck
								if(!r0.deck.isEmpty()){
									r0.t+=3;
									r0.event = DRAW;
								}
								else{
									//win check
									if(r0.num==0){
										winA = true; break;
									}
									//behave as THINK
									else{
										int j = r0.choice();
										if(j==-1){
											r0.event = WAIT;
											r0.t++;
										}
										else{
											r0.putPrepare();
										}
									}
								}
							}
							//put Robot B
							else{
								r0.t+=5;
								r0.event = BACK;
								r1.put();
								if(!r1.deck.isEmpty()){
									r1.t+=3;
									r1.event = DRAW;
								}
								else{
									if(r1.num==0){
										winA = false; break;
									}
									else{
										int j = r1.choice();
										if(j==-1){
											r1.event = WAIT;
											r1.t++;
										}
										else{
											r1.putPrepare();
										}
									}
								}
							}
						}
						else{
							//put Robot A
							r0.put();
							r1.put();
							//draw from deck
							if(!r0.deck.isEmpty()){
								r0.t+=3;
								r0.event = DRAW;
							}
							else{
								//win check
								if(r0.num==0){
									r0.event = WIN;
								}
								//behave as THINK
								else{
									int j = r0.choice();
									if(j==-1){
										r0.event = WAIT;
										r0.t++;
									}
									else{
										r0.putPrepare();
									}
								}
							}
							//put Robot B
							if(!r1.deck.isEmpty()){
								r1.t+=3;
								r1.event = DRAW;
							}
							else{
								if(r1.num==0){
									r1.event = WIN;
								}
								else{
									int j = r1.choice();
									if(j==-1){
										r1.event = WAIT;
										r1.t++;
									}
									else{
										r1.putPrepare();
									}
								}
							}
							//double win check
							if(r0.event == WIN && r1.event == WIN){
								winA = r0.lastCard.isStrong(r1.lastCard);
								break;
							}
							if(r0.event == WIN){
								winA = true; break;
							}
							if(r1.event == WIN){
								winA = false; break;
							}
						}
						continue;
					}
					if(r0.event == PUT){
						r0.put();
						//draw from deck
						if(!r0.deck.isEmpty()){
							r0.t+=3;
							r0.event = DRAW;
						}
						else{
							//win check
							if(r0.num==0){
								if(r1.event==WIN){
									winA = r0.lastCard.isStrong(r1.lastCard); break;
								}
								winA = true; break;
							}
							//behave as THINK
							else{
								int j = r0.choice();
								if(j==-1){
									r0.event = WAIT;
									r0.t++;
								}
								else{
									r0.putPrepare();
								}
							}
						}
						//process Robot B
						if(r1.event == DRAW){
							r1.draw();
							r1.event = THINK;
						}
						if(r1.event == BACK){
							r1.back();
							r1.event = THINK;
						}
						if(r1.event == WAIT){
							r1.event = THINK;
						}
						if(r1.event == WIN){
							winA = false; break;
						}
						if(r1.event == THINK){
							if(r1.num==3 && !r1.deck.isEmpty()){
								r1.t+=3;
								r1.event = DRAW;
							}
							else{
								int j = r1.choice();
								if(j==-1){
									r1.event = WAIT;
									r1.t++;
								}
								else{
									r1.putPrepare();
								}
							}
						}
						continue;
					}
					else if(r1.event == PUT){
						r1.put();
						if(!r1.deck.isEmpty()){
							r1.t+=3;
							r1.event = DRAW;
						}
						else{
							//win check
							if(r1.num==0){
								if(r0.event==WIN){
									winA = r0.lastCard.isStrong(r1.lastCard); break;
								}
								winA = false; break;
							}
							//behave as THINK
							else{
								int j = r1.choice();
								if(j==-1){
									r1.event = WAIT;
									r1.t++;
								}
								else{
									r1.putPrepare();
								}
							}
						}
						//process Robot A
						if(r0.event == DRAW){
							r0.draw();
							r0.event = THINK;
						}
						if(r0.event == BACK){
							r0.back();
							r0.event = THINK;
						}
						if(r0.event == WAIT){
							r0.event = THINK;
						}
						if(r0.event == WIN){
							winA = true; break;
						}
						if(r0.event == THINK){
							if(r0.num==3 && !r0.deck.isEmpty()){
								r0.t+=3;
								r0.event = DRAW;
							}
							else{
								int j = r0.choice();
								if(j==-1){
									r0.event = WAIT;
									r0.t++;
								}
								else{
									r0.putPrepare();
								}
							}
						}
						continue;
					}
				}
				//process only Robot A
				if(r0.t==T){
					if(r0.event == DRAW){
						r0.draw();
						r0.event = THINK;
					}
					if(r0.event == BACK){
						r0.back();
						r0.event = THINK;
					}
					if(r0.event == WAIT){
						r0.t++;
					}
					else if(r0.event == THINK){
						//draw from deck
						if(r0.num==3 && !r0.deck.isEmpty()){
							r0.event = DRAW;
							r0.t+=3;
						}
						else{
							int j = r0.choice();
							if(j==-1){
								r0.event = WAIT;
								r0.t++;
							}
							else{
								r0.putPrepare();
							}
						}
					}
					else if(r0.event == PUT){
						//put same place, so cancel Robot B
						if(r1.event == PUT && r0.leftSide!=r1.leftSide){
							r0.put();
							//draw from deck
							if(!r0.deck.isEmpty()){
								r0.t+=3;
								r0.event = DRAW;
							}
							else{
								//win check
								if(r0.num==0){
									r0.event = WIN;
								}
								//behave as THINK
								else{
									int j = r0.choice();
									if(j==-1){
										r0.event = WAIT;
										r0.t++;
									}
									else{
										r0.putPrepare();
									}
								}
							}
							r1.t = T+5;
							r1.event = BACK;
						}
						else {
							r0.put();
							//draw from deck
							if(!r0.deck.isEmpty()){
								r0.t+=3;
								r0.event = DRAW;
							}
							else{
								//win check
								if(r0.num==0){
									r0.event = WIN;
								}
								//behave as THINK
								else{
									int j = r0.choice();
									if(j==-1){
										r0.event = WAIT;
										r0.t++;
									}
									else{
										r0.putPrepare();
									}
								}
							}
						}
						if(r1.event == WAIT){
							r1.event = THINK;
						}
					}
					if(r0.event == WIN){
						winA = true; break;
					}
				}
				//process only Robot B
				if(r1.t==T){
					if(r1.event == DRAW){
						r1.draw();
						r1.event = THINK;
					}
					if(r1.event == BACK){
						r1.back();
						r1.event = THINK;
					}
					if(r1.event == WAIT){
						r1.t++;
					}
					else if(r1.event == THINK){
						if(r1.num==3 && !r1.deck.isEmpty()){
							r1.t+=3;
							r1.event = DRAW;
						}
						else{
							int j = r1.choice();
							if(j==-1){
								r1.event = WAIT;
								r1.t++;
							}
							else{
								r1.putPrepare();
							}
						}
					}
					else if(r1.event == PUT){
						if(r0.event == PUT && r0.leftSide!=r1.leftSide){
							r1.put();
							if(!r1.deck.isEmpty()){
								r1.t+=3;
								r1.event = DRAW;
							}
							else{
								if(r1.num==0){
									r1.event = WIN;
								}
								else{
									int j = r1.choice();
									if(j==-1){
										r1.event = WAIT;
										r1.t++;
									}
									else{
										r1.putPrepare();
									}
								}
							}
							r0.t = T+5;
							r0.back();
							int j = r0.choice();
							if(j==-1){
								r0.event = WAIT;
								r0.t++;
							}
							else{
								r0.putPrepare();
							}
						}
						else{
							r1.put();
							if(!r1.deck.isEmpty()){
								r1.t+=3;
								r1.event = DRAW;
							}
							else{
								if(r1.num==0){
									r1.event = WIN;
								}
								else{
									int j = r1.choice();
									if(j==-1){
										r1.event = WAIT;
										r1.t++;
									}
									else{
										r1.putPrepare();
									}
								}
							}
						}
						if(r0.event==WAIT){
							r0.event = THINK;
						}
					}
					if(r1.event == WIN){
						winA = false; break;
					}
				}
			}
			System.out.println(winA?"A wins.":"B wins.");
		}
	}
	
	public static void main(String[] args) {
		new AOJ2087().run();
	}
}