За время обучения не раз задавался вопросом: все эти хорошие и крутые алгоритмы чем могут помочь на практике? И вот, пару дней назад столкнулся с задачей по расчету цепи электрического тока. То, как это решилось и чем — под катом.
Скажу сразу, что ниже описана не полная версия алгоритма, о чем допишу в следующей части.
▍Постановка задачи
Задача читать из файла расположение элементов электрической цепи, считать сопротивление всей цепи и для каждого участка цепи установить силу тока, протекающего через него. Для упрощения примем, что элементы помещаются на плату 5х3 в свободные ячейки.
На первый раз ограничимся 4 элементами: резистором, батарейкой, лампочкой и пустышкой, которая соединяет участок цепи (без внутреннего сопротивления). Теперь, когда задача более/менее поставлена, можно приступать к реализации.
▍ Внутренние обозначения
Для определенности обозначим 0 за пустышку, 1 за резистор в 200 ом, 2 за 500 ом и 3 за 1к ом. Минус означает пробел. Батарейку и лампочку поставим заранее в точках 1,2 и 5,2 соответственно:
▍Чтение файла
Так как имя файла не константа, а алгоритм нужно сделать универсальным, то имя файла будет передаваемым параметром.
Вид уровня, хранящего с файле:
0 0 2 0 0
0 — - — 0
0 0 0 0 0
Считываем построчно (значит через массив строк) и разбираем потом отдельно каждую строку. Результат для каждого пустого места в плате вписываем в PlayerPrefs, чтобы можно было работать с элементами вне зависимости от исходной программы чтения из файла.
private string [] s;
private string str;
void Start()
{
s=System.IO.File.ReadAllLines (name);
r=s.Length;
i = 0;
while (i<s.Length) {
str=s[i];
j=0;
while(j<str.Length)
{
if(str[j]=='-') PlayerPrefs.SetInt((i+1).ToString()+«x»+((j-j%2)/2+1).ToString(),-1);
if(str[j]=='0') PlayerPrefs.SetInt((i+1).ToString()+«x»+((j-j%2)/2+1).ToString(),0);
if(str[j]=='1') PlayerPrefs.SetInt((i+1).ToString()+«x»+((j-j%2)/2+1).ToString(),1);
if(str[j]=='2') PlayerPrefs.SetInt((i+1).ToString()+«x»+((j-j%2)/2+1).ToString(),2);
if(str[j]=='3') PlayerPrefs.SetInt((i+1).ToString()+«x»+((j-j%2)/2+1).ToString(),3);
j++;j++;
}
i++;
}
}
▍Алгоритм обхода
Вот тут придется сделать оговорку. Так как работа со стеком вызвала некоторые сложности, реализация через очереди и стеки рассмотрю во второй части. Сейчас будем разбирать случай, когда нет никакого разветвления (крайне ограниченно, но для размеров 5х3 самое то).
Зададим начальное место обхода (у нас это от батарейки до другого входа в батарейку). Чтобы из каждой новой точки в плате не идти в обратную сторону, сделаем переменные, в которые будем записывать нашу предыдущую дислокацию. И ещё потребуется переменная, в которую будем запоминать все сопротивления, которые встретились. Для подстраховки сделаем переменную, которая будет отвечать за количество проделанных итераций на случай, если цепь окажется не замкнутой или в ней есть циклы.
Немного забежим вперед и ограничим перемещения по резисторам: если резистор расположен горизонтально (1-3), то тогда можно перемещаться только по вертикали, а если горизонтально (4-6 и об этом в следующей главе), то перемещения доступны только по горизонтали.
xn = 1;yn = 1;
r = 0;
step = 0; tick = 0;
while( ( !((xn==2)&&(yn==1)) )&&(step<500) )
{
step++;
p = PlayerPrefs.GetInt (xn.ToString()+«x»+yn.ToString());
if(p==1) r=r+200;
if(p==2) r=r+500;
if(p==3) r=r+1000;
PlayerPrefs.SetInt(xn.ToString()+«x»+yn.ToString()+«R»,r);
if(( ((xn+1)<=3) && (PlayerPrefs.GetInt ((xn+1).ToString()+«x»+yn.ToString())!=-1) )&&((xn+1)!=xr)&&((p==0)||(p>=4)) )
{
tick++;
xr=xn;
yr=yn;
xn=xn+1;
}
else
if(( ((xn-1)>=1) && (PlayerPrefs.GetInt ((xn-1).ToString()+«x»+yn.ToString())!=-1) )&&((xn-1)!=xr)&&((p==0)||(p>=4)) )
{
tick++;
xr=xn;
yr=yn;
xn=xn-1;
}
else
if(( ((yn+1)<=5) && (PlayerPrefs.GetInt (xn.ToString()+«x»+(yn+1).ToString())!=-1) )&&((yn+1)!=yr)&&(p<=4) )
{
tick++;
yr=yn;
xr=xn;
yn=yn+1;
}
else
if(( ((yn-1)>=1) && (PlayerPrefs.GetInt (xn.ToString()+«x»+(yn-1).ToString())!=-1) )&&((yn-1)!=yr)&&(p<=4))
{
tick++;
yr=yn;
xr=xn;
yn=yn-1;
}
}
Полученное сопротивление обработаем и сохраним в PlayerPrefs. Если сопротивление равно нулю, то заранее установим ток, равным 1 амперу. Если было совершено более 500 шагов, то в схеме есть цикл или же цепь не замкнута.
PlayerPrefs.SetInt («SxemeR», r);
if((step<500)&&(r!=0)) ir=9.0f/r; else
if(step>=500)
{
PlayerPrefs.SetInt («SxemeR», -1);
ir=0.001f;
}
Чтобы получить силу тока, разделим напряжение в батарейке (9В) на сопротивление в цепи. И запишем результат в каждый элемент цепи (для этого пройдем ещё раз по цепи по алгоритму, описанному выше).
▍В заключении первой части
— Описанные приемы позволяют создать систему, в которой пользователь сам планирует и размещает элементы цепи;
— Рассмотренный способ представляется интуитивно понятным, но не покрывает все потребности в расчетах;
— В следующей главе будет описано как обойти цепь, используя стек, чтобы не только упростить код, но и разрешить проблему с циклами и разветвлениями (параллельными соединениями в цепи);
P.S. Модели, используемые в проекте можно скачать тут.
Автор: Gr13