ビット演算を理解する

#なぜビット演算が必要なのか？

大きく次の２つのメリットがあります。
・多くのbool変数を１つの変数にまとめる事が出来る
・１つにまとまってるので操作が色々便利

例えば戦闘中のゲームのキャラクターの状態を示す以下のフラグ（bool型変数）があったとします。

```c#
bool isSleep;			//眠らされているか？
bool isPoison;			//毒に侵されているか？
bool isNoMagic;			//魔法を封じ込められているか？
bool isConfusion;		//混乱して敵味方の区別がつかないか？
bool isAttackDown;		//攻撃力がダウンしているか？
bool isDefDown;			//防御力がダウンしているか？
bool isAttackUp;		//攻撃力がアップしているか？
bool isDefUp;			//防御力がアップしているか？
```

ビット演算を使えば、全て１つの変数だけで制御できます。
ドラクエだとキャラクターの状態変化は数十もあるので１つ１つbool変数にしていたら面倒です。

そして回復魔法でマイナスの状態だけ（攻撃力・防御力アップ以外）を回復させるなど一括操作も簡単にできます。



このページの目的は、最後に載せているゲームのキャラクターの状態を管理するビット演算のコードを理解できるようにする事です。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/457922/d9c30c6e-5a8b-d3e6-93ec-6dd0bcc0c1b2.png" width=40%>


#だいたいこんな感じ

`0001`の眠りと、`0010`の毒を足すと、`0011`の毒に侵されて眠ってる状態が表現できます。
ただビット演算では、`+`や`-`ではなく、`|`や`&`や`~`などを使います。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/457922/09da3fe9-5967-6376-f7c9-b2242b987b63.png" width=60%>


#２進数の書き方
ビット演算するには２進数の書き方を覚えた方が便利です。ついでに１６進数の書き方も覚えましょう。

```c#
//全て31
var dec = 31;      //10進数
var bin = 0B11111;    //2進数（0Bか、0bを頭につける）16 + 8 + 4 + 2 + 1 = 31
var hex = 0X1F;     //16進数（0Xか、0xを頭につける）16 + 15 = 31

//C#7以降では見やすいように"_"で、区切る事が出来ます
var bin2 = 0B_0011_1111_1001;	//2進数
var hex2 = 0X_FF_2F_FF;			//16進数
```




#１０進数を２進数で表示
１０進数で返されたビット演算の結果は、２進数で確認した方がフラグの状態を理解しやすいです。
Convert.ToStringメソッドの第２引数に２を指定すると、第１引数を２進数に変換されます。
ちなみに１６だと１６進数に変換されます。
PadLeftメソッドは第１引数の桁になるまで、第２引数で文字列の左側を埋めます。

```c#
using System;

public class Prog {
    public static void Main() {
        int[] array = { 1, 2, 3, 4, 5, 0B111, 0XFF };

        foreach(    int i in array) {
            Console.WriteLine(  Convert.ToString(i, 2).PadLeft(8, '0')     + "  " + i);
        }
    }
}
//00000001  1
//00000010  2
//00000011  3
//00000100  4
//00000101  5
//00000111  7
//11111111  255
```

#ビット演算子

２つの数値を`ビット演算子`を使って計算します。１＋１＝２の`＋`みたいものです。
特に良く使う　`|`　と　`&`　はどっちがどっちか分からなくなるので、自分は次のイメージで覚えています。

#####`|`　は、片方だけでも１があれば、回転して残像で両方１になる。
#####`&`　は、両方の穴に１が収まって、はじめて１になる。


![Bit.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/457922/29d34ba9-4c4d-889c-5f22-dbd5d825c0d9.gif)



```c#
using System;

class Prog {
	static void Main(string[] args) {
		int a = 0B_0101;
		int b = 0B_0110;
		Print(		a&b		);	//両方１なら１　　　　　　論理積
		Print(		a|b		);	//どちらか１なら１	　　　論理和
		Print(		a^b		);	//片方だけ１なら１　　　　排他的論理和
		Print(		~a		);  //0なら1に、1なら0に　　　反転
											Console.WriteLine("");
		Print(		a<<1	);	//左にシフト
		Print(		a>>1	);	//右にシフト
	}
	static void Print(int i) {
		Console.WriteLine(		Convert.ToString(i, 2).PadLeft(4, '0'));
	}
}
//0100
//0111
//0011
//11111111111111111111111111111010

//1010
//0010
```
#良く使う演算子のパターン
ビット演算はかなり特殊なので、最初は分からなくても実際に使う段階で理解出来ます。こういうものかと深く考えずに暗記するのが早いです。

**左からn番目にフラグを立てる　`i | (1 << n)`**

```c#
int b = 1 << 2;		//0100		１０進数だと４を表します

int i = 0B_0001;
i |= (1 << 2);		//0101		１０進数だと５を表します
```

**同じ値をXOR 演算子　`^ `を使ってゼロクリアする。**

```c#
int i = 0B_0101;
i ^= i;		//0000
```

**左からn番目のフラグを消す　`i &= ~(1 << n)`**

```c#
int i = 0B_1111;
i &= ~(1 << 2);		//1011
```

**左からn番目のフラグが立っているか確認する　`i & (1 << n)`**

```c#
int i = 0B_0101;
Convert.ToBoolean(	i & (1 << 2));	//True
```

```c#
int i = 0B_0101;
bool b = (i & (1 << 2)) != 1;		//True
```

**複数のフラグをまとめて、マスクビットを作る**

```c#
int a = (1 << 1);		//0010
int b = (1 << 2);		//0100
var mask = a |= b;		//0110

int c = 0B_1111;		//1111
c &= ~mask;				//1001
```


#enumを使ってビット演算を見やすく

ビット演算は便利ですが、フラグを数値だけで管理すると分からなくなりそうです。（0B_0100は「毒」で、0B_1000は「混乱」など自分で決めても間違えそうです‥）
enumの前に、FlagsAttribute属性を指定することで、列挙型の名前でビット演算のフラグを管理出来ます。（Flagsを外すと数値に対してビット演算しません`[Flags]`をコメントにすると違いが分かります）

```c#
using System;

class Prog{
	[Flags]			//enumをビットフラグにするFlagsAttribute属性
	enum Col:short {
		Red =	1 << 0,		//0001
		Green =	1 << 1,		//0010
		Blue =	1 << 2,		//0100
	};
	static void Main() {

		for(int i = 0; i <= 8; i++)
			Console.WriteLine("{0}  {1}  {2}",
				i,
				Convert.ToString(i, 2).PadLeft(4, '0'),
				(Col)i);
	}
}
//0  0000  0
//1  0001  Red
//2  0010  Green
//3  0011  Red, Green
//4  0100  Blue
//5  0101  Red, Blue
//6  0110  Green, Blue
//7  0111  Red, Green, Blue
//8  1000  8
```
ちなみに自然界では、赤と緑をかけ合わせると黄色になり、赤と青をかけ合わせると紫色になり、赤、緑、青、全てをかけ合わせると無色になります。三原色をビット演算で組み合わせる事で虹色も表現する事ができます。

**１つ具体例として、Taskのオブションで使うフラグの例を見てみましょう。**
`TaskCreationOptions.LongRunning | TaskCreationOptions.PreferFairness`は、列挙型のフラグです。


```c#
var task = new Task(	() => Method_A(),
					TaskCreationOptions.LongRunning | TaskCreationOptions.PreferFairness);
task.Start();
```

[TaskCreationOptions 列挙型](https://docs.microsoft.com/ja-jp/dotnet/api/system.threading.tasks.taskcreationoptions?view=net-5.0)

列挙型の説明を見ると、フィールド毎に番号がふってありますが、全て２の倍数になっています。
この整数値をビット演算で組み合わせて、１つの引数で複数のフラグを立てる事を可能にしています。番号だけだと分かりにくいので列挙型にして名前を付けている訳です。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/457922/dbf9a205-0f26-4dbd-84f7-a162def501db.png" width=50%>









#ゲームのキャラクターの戦闘状態を制御

ビット演算の締めくくりに、ゲームの戦闘中のキャラクターの状態を次々と変化させていきましょう。


```c#
using System;

[Flags]
public enum Status {		//１０進数で書く場合は、２の倍数にする
	Normal = 0,
	//マイナスの状態異常
	Sleep = 1,		//眠っている				0001
	Poison = 2,		//毒に侵されている			0010
	//プラスの状態変化
	AttackUp = 4,	//魔法で攻撃力アップ		0100
	DefUp = 8,		//魔法で防御力アップ		1000
}

public class Prog {
    public static void Main() {

        var chara = Status.Normal;  //キャラクター

        chara |= Status.AttackUp;   //味方の魔法で、攻撃力アップ！
        Print(  chara);             //AttackUp

        chara |= Status.Poison;     //敵の毒に侵される！
        Print(  chara);             //Poison, AttackUp

        chara |= Status.Sleep;      //敵に眠らされる！
        Print(  chara);             //Sleep, Poison, AttackUp

        //回復魔法（眠りと毒だけを解除、攻撃力・防御力アップはそのまま）
        Status heal = Status.Sleep | Status.Poison;

        chara &= ~heal;             //味方の回復魔法で復活！
        Print(  chara);             //AttackUp

        //攻撃力・防御力どちらか、もしくは両方がアップしているか確認
        Status up = Status.AttackUp | Status.DefUp;
        bool b = (chara & up) != Status.Normal;     //True  
	
        chara ^= chara;				//全ての効果を無効にする魔法を唱える！
        Print(  chara);				//Normal
    }
	//状態を確認するメソッド
	static void Print(	Status s){
		Console.WriteLine(	"{0}  {1}", Convert.ToString(	(int)s, 2).PadLeft(4,'0'), s);
	}
}
```

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/457922/32c0ca63-0ccd-8dc8-ab15-a8351a6aa59f.png" width=40%>