using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static string InputPattern = "InputX";
static List<string> GetInputList()
{
var WillReturn = new List<string>();
if (InputPattern == "Input1") {
WillReturn.Add("3 3");
WillReturn.Add("s..");
WillReturn.Add(".a.");
WillReturn.Add("..g");
//5
}
else if (InputPattern == "Input2") {
WillReturn.Add("4 4");
WillReturn.Add("s...");
WillReturn.Add(".a#.");
WillReturn.Add("....");
WillReturn.Add("###g");
//13
}
else if (InputPattern == "Input3") {
WillReturn.Add("4 4");
WillReturn.Add("...a");
WillReturn.Add(".s..");
WillReturn.Add("..g.");
WillReturn.Add("....");
//-1
}
else {
string wkStr;
while ((wkStr = Console.ReadLine()) != null) WillReturn.Add(wkStr);
}
return WillReturn;
}
static int UB_X;
static int UB_Y;
static void Main()
{
List<string> InputList = GetInputList();
char[,] BaseBanArr = CreateBanArr(InputList.Skip(1));
UB_X = BaseBanArr.GetUpperBound(0);
UB_Y = BaseBanArr.GetUpperBound(1);
// 片付け先の座標
int Goal_X = -1;
int Goal_Y = -1;
// すぬけ君の座標
int S_X = -1;
int S_Y = -1;
// 荷物の座標
int A_X = -1;
int A_Y = -1;
for (int X = 0; X <= UB_X; X++) {
for (int Y = 0; Y <= UB_Y; Y++) {
if (BaseBanArr[X, Y] == 'g') {
BaseBanArr[X, Y] = '.';
Goal_X = X;
Goal_Y = Y;
}
if (BaseBanArr[X, Y] == 'a') {
A_X = X;
A_Y = Y;
}
if (BaseBanArr[X, Y] == 's') {
S_X = X;
S_Y = Y;
}
}
}
var Que = new Queue<JyoutaiDef>();
JyoutaiDef WillEnqueue;
WillEnqueue.A_X = A_X;
WillEnqueue.A_Y = A_Y;
WillEnqueue.S_X = S_X;
WillEnqueue.S_Y = S_Y;
WillEnqueue.Level = 0;
Que.Enqueue(WillEnqueue);
var VisitedSet = new HashSet<long>();
VisitedSet.Add(GetHash(WillEnqueue));
while (Que.Count > 0) {
JyoutaiDef Dequeued = Que.Dequeue();
// クリア判定
if (Dequeued.A_X == Goal_X && Dequeued.A_Y == Goal_Y) {
Console.WriteLine(Dequeued.Level);
return;
}
Action<int, int> EnqueueAct = (pVectX, pVectY) =>
{
int NewX = Dequeued.S_X + pVectX;
int NewY = Dequeued.S_Y + pVectY;
// 盤外の場合
if (NewX < 0 || UB_X < NewX) return;
if (NewY < 0 || UB_Y < NewY) return;
// 壁の場合
if (BaseBanArr[NewX, NewY] == '#') return;
// 空きマスの場合
if (NewX != Dequeued.A_X || NewY != Dequeued.A_Y) {
WillEnqueue.A_X = Dequeued.A_X;
WillEnqueue.A_Y = Dequeued.A_Y;
WillEnqueue.S_X = NewX;
WillEnqueue.S_Y = NewY;
long Hash = GetHash(WillEnqueue);
if (VisitedSet.Add(Hash)) {
WillEnqueue.Level = Dequeued.Level + 1;
Que.Enqueue(WillEnqueue);
}
}
// 荷物がある場合
if (NewX == Dequeued.A_X && NewY == Dequeued.A_Y) {
int NewX2 = Dequeued.S_X + pVectX * 2;
int NewY2 = Dequeued.S_Y + pVectY * 2;
// 盤外の場合
if (NewX2 < 0 || UB_X < NewX2) return;
if (NewY2 < 0 || UB_Y < NewY2) return;
// 壁の場合
if (BaseBanArr[NewX2, NewY2] == '#') return;
WillEnqueue.S_X = NewX;
WillEnqueue.S_Y = NewY;
WillEnqueue.A_X = NewX2;
WillEnqueue.A_Y = NewY2;
long Hash = GetHash(WillEnqueue);
if (VisitedSet.Add(Hash)) {
WillEnqueue.Level = Dequeued.Level + 1;
Que.Enqueue(WillEnqueue);
}
}
};
EnqueueAct(0, -1);
EnqueueAct(0, 1);
EnqueueAct(-1, 0);
EnqueueAct(1, 0);
}
Console.WriteLine(-1);
}
struct JyoutaiDef
{
internal int A_X;
internal int A_Y;
internal int S_X;
internal int S_Y;
internal int Level;
}
// 盤面のハッシュ値を求める
static long GetHash(JyoutaiDef pJyoutai)
{
long A_X = pJyoutai.A_X;
long A_Y = pJyoutai.A_Y;
long S_X = pJyoutai.S_X;
long S_Y = pJyoutai.S_Y;
long Hash = 0;
Hash += A_X * 1000000000;
Hash += A_Y * 1000000;
Hash += S_X * 1000;
Hash += S_Y;
return Hash;
}
////////////////////////////////////////////////////////////////
// IEnumerable<string>をcharの2次元配列に設定する
////////////////////////////////////////////////////////////////
static char[,] CreateBanArr(IEnumerable<string> pStrEnum)
{
var StrList = pStrEnum.ToList();
if (StrList.Count == 0) {
return new char[0, 0];
}
int UB_X = StrList[0].Length - 1;
int UB_Y = StrList.Count - 1;
char[,] WillReturn = new char[UB_X + 1, UB_Y + 1];
for (int Y = 0; Y <= UB_Y; Y++) {
for (int X = 0; X <= UB_X; X++) {
WillReturn[X, Y] = StrList[Y][X];
}
}
return WillReturn;
}
}